Main Site Documentation

Threading Problems?


#1

Referring to this forum thread http://www.tinyclr.com/forum/7/1140/ I am running the IMU sampling and filtering process in a separate thread. My main thread sleeps infinitely after initializing all peripherals and starting the IMU thread. Incoming data from XBee is processed as an interrupt. All these work perfectly while debugging on the USB cable.

However when I tried running the system standalone, powering up with a 5V power source and try running the Artificial Horizon again, it didn’t tilt as what we see on that video clip. Instead it stays there as it wasn’t receiving any signals from XBee at all.

I run X-CTU to see the raw data, and found that all data transmitted is 0, which means the IMU thread wasn’t running at all.

What would be the problem?


#2

The standard question at this point is always “did you check the 5v power”?

Where did u connect the 5v.


#3

I tried different approaches… Supplying 5v to the 5v pin… Plugging in a 9v battery to the dc jack… Nothing… All other functions work correctly but the IMU thread.


#4

Are using serial? Data Rx event?


#5

XBee over serial. RX event is fired whenever there is data.

Posting Program.cs…

using System;
using System.Text;
using System.IO.Ports;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

using GHIElectronics.NETMF.FEZ;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.System;

using SCSUQuadcopter.Drivers;

namespace SCSUQuadcopter
{
    public class Program
    {
        #region Hardware
        /// <summary>
        /// Hardware initializations
        /// </summary>
        public static IMU pIMU = new IMU((FEZ_Pin.Digital)FEZ_Pin.Digital.Di10);
        private static SerialPort pZigBee = new SerialPort("COM1", 115200, Parity.None, 8, StopBits.One);
        private static SpeedController[] pMotor = new SpeedController[4];
        //static AnalogIn pBattery = new AnalogIn((AnalogIn.Pin)FEZ_Pin.AnalogIn.An0);

        //Declearing the interrupt pin 
        private static InterruptPort iRPM1 = new InterruptPort((Cpu.Pin)FEZ_Pin.Digital.An0, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeHigh);
        #endregion

        #region Constants
        public const byte ROLL = 0;
        public const byte PITCH = 1;
        public const byte YAW = 2;
        #endregion

        #region Vars
        /// <summary>
        /// variables to hold values
        /// </summary>
         public static double deltaT;
        private static long currentTime;
        private static long previousTime;
        private static byte[] imustring;
        private static bool imuDataReady = false;
        private static imuFuse tiltEstimate = new imuFuse();
        private static double roll;
        private static bool ctrl = false;

        // Declare the RPM_1 variable
        private static byte[] bRPM1 = new byte[16];
        private static int time2_RPM1 = 0;
        private static int time1_RPM1 = 0;
        private static int period1 = 0;
        #endregion

        #region Threads
        /// <summary>
        /// Threads defined here
        /// </summary>
        static Thread SelfBalancing = new Thread(selfBalance);
        #endregion


        public static void Main()
        {
            // Initialize Zigbee
            pZigBee.Open();
            pZigBee.DataReceived += new SerialDataReceivedEventHandler(pZigBee_DataReceived);


            pMotor[0] = new SpeedController(FEZ_Pin.PWM.Di9);
            pMotor[1] = new SpeedController(FEZ_Pin.PWM.Di8);
            pMotor[2] = new SpeedController(FEZ_Pin.PWM.Di6);
            pMotor[3] = new SpeedController(FEZ_Pin.PWM.Di5);

            // Declaring the interrupt for Motor #1
            iRPM1.OnInterrupt += new NativeEventHandler(iRPM1_OnInterrupt);

            //byte[] testresult = Encoding.UTF8.GetBytes(pIMU.selfTest().ToString());
            //pZigBee.Write(testresult, 0, testresult.Length);

            previousTime = 0;

            SelfBalancing.Priority = ThreadPriority.Normal;
            SelfBalancing.Start();
            
            Thread.Sleep(Timeout.Infinite);
        }

        static void selfBalance()
        {
            #region Using Starlino's Algorithm

            //previousTime = 0;
            while (true)
            {
                //if (!imuDataReady)
                {
                    Debug.Print("IN the LOOP");
                    currentTime = DateTime.Now.Ticks;
                    deltaT = (currentTime - previousTime) * 1e-7;
                    previousTime = currentTime;
                    pIMU.getData();
                    tiltEstimate.updateVec();
                    imuDataReady = true;
                    //Thread.Sleep(0);
                }

                roll = MathEx.Atan2(tiltEstimate.R_est.y, -tiltEstimate.R_est.z) * 180.0 / MathEx.PI;

                if (ctrl)
                {
                    if (roll > 3)
                    {
                        pMotor[1].Throttle++;
                    }
                    else
                    {
                        pMotor[1].Throttle--;
                    }
                }

                Thread.Sleep(10);
            }

            #endregion
        }

        static void pZigBee_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // Place a Delay here so that there is enough time for the data to be fully transfered
            Thread.Sleep(2);
            byte[] inbuff = new byte[pZigBee.BytesToRead];
            pZigBee.Read(inbuff, 0, inbuff.Length);
            string[] received = ((new string(Encoding.UTF8.GetChars(inbuff))).Split('\n')[0].Split(','));

            // check recieved command nature
            switch (received[0])
            {
                case "ARM":  // if received is an ARMing command
                    short arming = short.Parse(received[1]);
                    if ((arming & 0x01) == 0x01) // if bit0 is 1
                        pMotor[0].Arm();
                    if ((arming & 0x02) == 0x02) // if bit1 is 1
                        pMotor[1].Arm();
                    if ((arming & 0x04) == 0x04) // if bit2 is 1
                        pMotor[2].Arm();
                    if ((arming & 0x08) == 0x08) // if bit3 is 1
                        pMotor[3].Arm();
                    Thread.Sleep(2000);
                    break;

                case "THR":  // if received is a throttle command
                    for (int i = 0; i < 4; i++)
                    {
                        if (pMotor[i].isArmed)
                        {
                            pMotor[i].Throttle = double.Parse(received[i + 1]);
                        }
                    }
                    break;

                case "CTL":
                    ctrl = true;
                    break;

                case "IMU":
                    //if (imuDataReady)
                    {
                        imustring = Encoding.UTF8.GetBytes("IMU," + IMUData.XAccel.ToString() + "," + IMUData.YAccel.ToString() + "," + IMUData.ZAccel.ToString() + "," + tiltEstimate.R_est.x.ToString() + "," + tiltEstimate.R_est.y.ToString() + "," + tiltEstimate.R_est.z.ToString() + "\n");
                        pZigBee.Write(imustring, 0, imustring.Length);
                        imuDataReady = false;
                    }
                    break;
            }

            //bRPM1 = Encoding.UTF8.GetBytes("RPM," + time1_RPM1.ToString() + "," + period1.ToString() + "," + pMotor[0].Throttle.ToString() + "\n");
            
            // Acknowledge the Remote that process is complete
            //pZigBee.Write(bRPM1, 0, bRPM1.Length);


            // inbuff is not needed anymore, discard it
            inbuff = null;

            // So as received
            received = null;

            // bRPM1 too...
            bRPM1 = null;
            imustring = null;
        }


        static void iRPM1_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            time2_RPM1 = (int)time.Ticks;
            period1 = time2_RPM1 - time1_RPM1;
            time1_RPM1 = time2_RPM1;
        }
    }
}


#6

I have absolutely no idea why your program works in debug mode and not in stand alone. Have you tried to just power it from USB without being in debug mode?

But looking at your code I feel there is something wrong.
When pZigBee_DataReceived fires there is some activity on the serial channel but one can not know if it’s a full sentence or just fractions. It can be 100 bytes, it can be 9 bytes but most often it is only 1 byte. As far as i can see you read the buffer and tries to split at ‘\n’ Then if its not a full sentence you discharge the string and the buffer is lost.

This is a snip from my Whirligig code;

void XBee_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (e.EventType == SerialData.Chars)
            {
                byte[] ReadBuffer = new byte[XBeeHandle.BytesToRead];
                XBeeHandle.Read(ReadBuffer, 0, ReadBuffer.Length);
                XBee_line = XBee_line + new String(Encoding.UTF8.GetChars(ReadBuffer));

                if (XBee_line.IndexOf('\n') > 0)
                {
                    m_Command = true;
                    m_line = XBee_line.Substring(0, XBee_line.IndexOf('\n') - 1);
                    XBee_line = "";
                }

            }
        }

As you can see, I’m not handling the input here. Only setting a flag if we have a complete sentence and the sentence is available in m_line.


#7

I can’t remember the right way but try swaping these

// Initialize Zigbee
pZigBee.Open();
pZigBee.DataReceived += new SerialDataReceivedEventHandler(pZigBee_DataReceived);

change to

 // Initialize Zigbee
pZigBee.DataReceived += new SerialDataReceivedEventHandler(pZigBee_DataReceived);
pZigBee.Open();


#8

see… it’s not Xbee that has problems… it’s the selfBalance Thread guys…


#9

@ Gus
I beg to differ, but in your GPS driver you state

_port.Open();// If you open the port after you set the event you will endup with problems
_port.DataReceived += new SerialDataReceivedEventHandler(_port_DataReceived);

So the event handler should be attached after the port i opened.


#10

Maybe in a debug scenario the processing time is so slow that you actually get full sentences with your current setup? I have no idea.
But if your system works in debug mode, but not when just powered from USB i would think it has something to do with the debugger.


#11

I actually run this a couple times, changing PWM pulse widths over Zigbee and reading motor measurements without any problem. So it won’t be the serial. And I do get the data from IMU, sent via zigbee, but it’s just zeroes.


#12

hiya,

9v …you maybe compunding issues, not sure I’d try doing too much wireless stuff using 9V anymore. what’s your power budget like? 9V 300-600mAH but you will never get anything like that as usable energy.

I’ve been doing some wifi struff on another MCU and binned alot of power ideas in favour of a 2A boost regulator with AA rechargeables 2500mAH much better than some of the other power ideas I had. things work much better.

On threading, in c# .net you need to use an invoke method or the connection blocks…nothing comes through until the connections is dropped if you are lucky. So not sure if that is applicable in netmf.

Dave


#13

so after nights of debugging I actually came to a conclusion that the IMU was causing all the frustration and confusion :-[

Thank you everyone who were trying to help… very appreciate that even they don’t actually solve the problem…

Gotta call Analog Devices the first thing tomorrow…