Main Site Documentation

How to share data between threads?


#1

Still playing with FEZ Domino and have about 20K of compiled application, but when I use get / set method for sharing live data across my threads I have memory run out.

This is a part of the code that causes the issue:


            public static byte PWMDuty  // Share PWMDuty value across my threads.
            // On FEZ Domino this method causes memory leak.
            {
                get { return PWMDuty; }
                set {  PWMDuty = value; }
            }

In C# this method works smoothly but under FEZ Domino it makes me buy FEZ Cobra to have more memory.

Bellow is the dump:


#2

You can use “lock” statement. Having a memory leak by just using set/get method sounds strange you don’t allocate anything there. The leak must be somewhere else.


#3

It looks creepy but this is what happens. When I comment out the method everything runs just fine.

Can I send you my project files to take look at?


#4

Here is my Program.cs content besides four driver files I call out from the Main()


using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

/// my classes
using DS18B20;
using BMP085_SENSOR;

namespace FEZ_DOMINO_HHO    
{
    public class Program
    {
        public static void Main()
        {
            RTC_init();
            
            //Thread BatteryVoltageMonitoring_Monitor = new Thread(BatteryMonitor.Fixed.VoltageMonitoring_Thread);
            //BatteryVoltageMonitoring_Monitor.Start();

            Thread DS18B20_Monitor = new Thread(DS18B20_PWM_Thread);
            DS18B20_Monitor.Start();

           // Thread BMP085_Monitor = new Thread(BMP085_Thread);
           // BMP085_Monitor.Start();

            Thread SD_AutoMount_Monitor = new Thread(SD_AutoMount_Thread);
            SD_AutoMount_Monitor.Start();

            Thread USB_AutoMount_Monitor = new Thread(USB_AutoMount_Thread);
            USB_AutoMount_Monitor.Start();

            Thread LEDPOWER_Monitor = new Thread(LEDPOWER_Thread);
            LEDPOWER_Monitor.Start();


        }


        private static void RTC_init()
        {
            // To keep track of time, 
            // setting it at the beginning of application from the RTC.
            // If it was NOT set before and currently running using 
            // the battery (not exhausted), set it to a fixed time.
            DateTime deviceDateTime = RealTimeClock.GetTime();
            Debug.Print("Getting device's RTC:\t" + deviceDateTime.ToString());
            int rtcFirmwareTime = deviceDateTime.GetHashCode();
            // Debug.Print(rtcYear.ToString()); // getting a printout of default hash code for firmware set time and date.
            if (rtcFirmwareTime == 1243046445) // comparing with hash code of default firmware set time.
                RealTimeClock.SetTime(new DateTime(2011, 01, 18, 12, 0, 0, 0)); // year, month, day, hour, minute, second, millisecond
            Utility.SetLocalTime(RealTimeClock.GetTime());
            deviceDateTime = RealTimeClock.GetTime();
            Debug.Print("Retrieving reset RTC:\t" + deviceDateTime.ToString() + "\r\n");
            /// End setting up date and time   
        } // Initializing system RTC

        public class PWM_data
        {
            public class Fixed
            {
                // Initializing PWM values 

                // max frequency for Power Mosfets:
                // 55V/52A - IRLI3705N Mosfet - typical: 4000 KHz,
                // 125A/24V - NTB125N02R / NTP125N02R Mosfet - max delay: 5494 KHz, typical: 10200 KHz.            

                // DO NOT RAISE FREQUENCY ON USBizi CHIPSETS OVER 5 Mhz! It is BUGGY! WHAT IS SAFE MAX PWM FREQ THAT CAN BE USED?
                // Can set up to 1000000 Hz with set duty cycles of 100, 80, 70, 60, 50, 40, 30, 20%. Does not work on 90% cycle.

                // 10^9 /(freq in Hz) == period of the pulse (in ns) == period_nanosecond
                // period_nanosecond*duty (between 0 and 100%) == highTime_nanosecond
                // 12.34kHz, 56% positive duty cycle would use 
                // pwm.setPulse(81037,45380);

                // Frequency ranges to try to find best HHO production freq range
                // 100 KHz to 120 KHz 
                // 40 KHz to 44 KHz 
                // 20 KHz to 24 KHz 
                // 9 KHz to 12 KHz 
                // 23.254 Khz / 46.508 khZ - seen at http://hho-blog.info/blog/post/LCD-screen-for-HHO-system.aspx
                // 21.4 & 42.8 mixed Frequency ???

                public const int PWMfrequency = 23254; // Frequency in Hz. Possible resonance freq. of water - 42KHz, 454.177 KHz or 2.5 GHz
                public const int PWMfrequency2 = PWMfrequency * 2; // Frequency in Hz. Possible resonance freq. of water - 42KHz, 454.177 KHz or 2.5 GHz
                public const int PWMToggleDelay = 2500; // PWM frequencies toggle delay in ms
                public const byte DutyDivider = 2; // Min divider - 2, max divider - 8
                    // PWM Duty Cycle scale down divider. For future use with AMPERAGE MONITOR / ADJUSTER
                public static byte[] DutyTrigger = new byte[8] { 80, 100, 150, 200, 200, 160, 100, 100 }; // pwm duty cycle limits.
                    // Multiply by 2, then it is divided by DutyDivider TO GET BETTER DOWNSCALING ON THE GO.
                public static byte[] DutyLimit = new byte[8] { DutyTrigger[0] /= DutyDivider, DutyTrigger[1] /= DutyDivider, DutyTrigger[2] /= DutyDivider,
                    DutyTrigger[3] /= DutyDivider, DutyTrigger[4] /= DutyDivider, DutyTrigger[5] /= DutyDivider, DutyTrigger[6] /= DutyDivider, DutyTrigger[7] /= DutyDivider };
                
                public static PWM pwm = new PWM((PWM.Pin)FEZ_Pin.PWM.Di8); // PWM on Digital Pin 8
                public static PWM pwm_duplicate = new PWM((PWM.Pin)FEZ_Pin.PWM.Di9); // Duplicate PWM on Digital Pin 9
                public static int[] TempLimit = new int[8] { -20, -10, 0, 34, 42, 50, 55, 60 }; // temperature limits in Celsius
            
            }

            //public static byte PWMDuty = 0;

            public static byte PWMDuty  // Share PWMDuty value across my threads.
            // On FEZ Domino this method causes memory leak.
            {
                get { return PWMDuty; }
                set { PWMDuty = value; }
            }

        } // Shareable PWM data classes

        public static void LEDPOWER_Thread()
        {
              OutputPort LED;
            LED = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, true);
            while (true)
            {
                LED.Write(!LED.Read());
                Thread.Sleep(500);
            }
        } // Power LED

        public static void DS18B20_PWM_Thread() // Monitoring DS18B20 sensor data and adjusting PWM
        {
            System.TimeSpan uptimeCount;
            DateTime startupTime = RealTimeClock.GetTime(); // defining startup time variable

            var w = DS18B20.DS18B20.cTemp ? new ThermometerWatcher(new Thermometer((Cpu.Pin)FEZ_Pin.Digital.An1), Thermometer.DataToC) :
                            new ThermometerWatcher(new Thermometer((Cpu.Pin)FEZ_Pin.Digital.An1), Thermometer.DataToF);

            // defining Output Port for switching on/of Relay Mosfet for Coolant Fan
            OutputPort CoolantLineFan;
            CoolantLineFan = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di13, false);

            int i;

            w.TemperatureChanged += (from, to) =>
            {
                // Adjusting Duty Cycle of PWM according to Temperature Changes

                double double_temp = DS18B20.DS18B20.cTemp ? to : ((to - 32) / 1.8);

                for (i = PWM_data.Fixed.DutyLimit.Length; i > 0; i--)
                {
                    i -= 1;
                    if (double_temp <= PWM_data.Fixed.TempLimit[i])
                    {
                        PWM_data.PWMDuty = PWM_data.Fixed.DutyLimit[i];
                    }
                    else if (double_temp >= PWM_data.Fixed.TempLimit[PWM_data.Fixed.TempLimit.Length-1])
                    {
                        PWM_data.PWMDuty = 0; Debug.Print("HHO CELL IS OVERHEATED. TEMPERATURE IS " + to + DS18B20.DS18B20.TempSymbol + ". TEMPORARY SHUTOFF."); 
                    }
                }  
                

                if (double_temp >=PWM_data.Fixed.TempLimit[5])
                {
                    if (CoolantLineFan.Read())
                    return;
                    else
                    {
                    CoolantLineFan.Write(true);
                    Debug.Print("Cell Temperature is " + to + DS18B20.DS18B20.TempSymbol + ". Switching ON Coolant Fan.");
                    }
                } else
                {
                    if (CoolantLineFan.Read() == true)
                    {
                        if (double_temp <=PWM_data.Fixed.TempLimit[4])
                        {
                            CoolantLineFan.Write(false);
                            Debug.Print("Cell Temperature dropped to " + to + DS18B20.DS18B20.TempSymbol + ". Switching OFF Coolant Fan.");
                        }
                    }
                }

                Debug.Print("Time Stamp: " + DateTime.Now);
                Debug.Print("DS18B20 Sensor: Temperature = " + to + DS18B20.DS18B20.TempSymbol);
                Debug.Print("PWM Frequency: " + (PWM_data.Fixed.PWMfrequency) + " Hz. PWM Duty Cycle: " + PWM_data.PWMDuty + "%.");
                uptimeCount = DateTime.Now.Subtract(startupTime); // Counting System Uptime and sending out to Debug Print
                Debug.Print("System Uptime: " + uptimeCount.Hours.ToString() + " hours " + uptimeCount.Minutes.ToString() + " minutes\r\n"); // Counting System Uptime and sending out to Debug Print 

                if (PWM_data.PWMDuty != 0)
                    PWM_data.PWMDuty -= 1;
                                
                PWM_data.Fixed.pwm.Set(PWM_data.Fixed.PWMfrequency, PWM_data.PWMDuty); // Output to Power Mosfets
                PWM_data.Fixed.pwm_duplicate.Set(PWM_data.Fixed.PWMfrequency, PWM_data.PWMDuty); // Duplicate output to control the PWM signal
                   

                   // Debug.Print("PWM Freq. now " + PWM_data.Fixed.PWMfrequency + " Hz for " + PWM_data.Fixed.PWMFreqToggleDelay / 1000 + " seconds.");
                   // Thread.Sleep(PWM_data.Fixed.PWMFreqToggleDelay);

                    Thread.Sleep(500);
            };

            w.Start();

            Thread.Sleep(Timeout.Infinite);
        }

        private static void BMP085_Thread()
        {

            //Use maximum precision (and maximum power use), auto acquire every 10s
            BMP085_SENSOR.BMP085 MyBMP085 = new BMP085_SENSOR.BMP085(Oversampling.Level3, 10);
            if (!MyBMP085.Ok) throw new Exception("Unable to access the BMP085 sensor");
            Thread.Sleep(50); // Wait for Sensor to make measurement

            OutputPort CompartmentFan;
            CompartmentFan = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di12, false);
            int[] CompartmentTemp = new int[2] { 25, 20 }; // Compartment threshold temperature points in C.

            int alt = 0;
            float tem = 0;
            while (true)
            {
                if (MyBMP085.Ok)    // sensor values reading
                {
                    if (MyBMP085.Altitude != alt | MyBMP085.Temperature != tem)
                    {
                        if (DS18B20.DS18B20.cTemp)
                        {
                            Debug.Print("Time Stamp: " + DateTime.Now);
                            Debug.Print("BMP085 Sensor: Temperature = " + MyBMP085.Temperature.ToString("F1") + "° C, " +
                            "Pressure = " + (MyBMP085.Pressure) + " Pa, " + "Altitude = " + ((MyBMP085.Altitude) + " meters.\r\n"));

                            double MyBMP085_Temp = MyBMP085.Temperature;
                            if (MyBMP085_Temp >= CompartmentTemp[0])
                            {
                                if (CompartmentFan.Read())
                                    return;
                                else
                                {
                                    CompartmentFan.Write(true);
                                    Debug.Print("Compartment Temperature is " + MyBMP085.Temperature.ToString("F1") + DS18B20.DS18B20.TempSymbol + ". Switching ON Cooling Fan.");
                                }
                            }
                            else
                            {
                                if (CompartmentFan.Read() == true)
                                {
                                    if (MyBMP085_Temp <= CompartmentTemp[1])
                                    {
                                        CompartmentFan.Write(false);
                                        Debug.Print("Compartment Temperature dropped to " + MyBMP085.Temperature.ToString("F1") + DS18B20.DS18B20.TempSymbol + ". Switching OFF Cooling Fan.");
                                    }
                                }
                            }

                        }
                        else
                        {
                            Debug.Print("Time Stamp: " + DateTime.Now);
                            Debug.Print("BMP085 Sensor: Temperature = " + (32 + (9 / 5) * (MyBMP085.Temperature / 16)).ToString("F1") + "° F, " +
                            "Barometric Pressure = " + (MyBMP085.Pressure) + " Pa, " +
                            "Altitude = " + (((MyBMP085.Altitude) * 3.2808) + " feet.\r\n"));

                            double MyBMP085_Temp = MyBMP085.Temperature;
                            if (MyBMP085_Temp >= CompartmentTemp[0])
                            {
                                if (CompartmentFan.Read())
                                    return;
                                else
                                {
                                    CompartmentFan.Write(true);
                                    Debug.Print("Compartment Temperature is " + MyBMP085.Temperature.ToString("F1") + DS18B20.DS18B20.TempSymbol + ". Switching ON Cooling Fan.");
                                }
                            }
                            else
                            {
                                if (CompartmentFan.Read() == true)
                                {
                                    if (MyBMP085_Temp <= CompartmentTemp[1])
                                    {
                                        CompartmentFan.Write(false);
                                        Debug.Print("Compartment Temperature dropped to " + MyBMP085.Temperature.ToString("F1") + DS18B20.DS18B20.TempSymbol + ". Switching OFF Cooling Fan.");
                                    }
                                }
                            }

                        }
                        alt = MyBMP085.Altitude;
                        tem = MyBMP085.Temperature;
                    }
                }
                else Debug.Print("Unable to access the BMP085 sensor !");
                Thread.Sleep(5000);
            }
        } // Monitor Ambient Temperature / Pressure sensor (BMP085)

        public class BatteryMonitor // Monitor Vehicle's Voltage output
        {
            public static int currentVehicleAlternatorVoltage  // Share Voltage value across my threads.
            // On FEZ Domino this method causes memory leak.
            {
                get { return currentVehicleAlternatorVoltage; }
                set { currentVehicleAlternatorVoltage = value; }
            }

            public class Fixed
            {
                    // Init values for battery voltage monitoring, Cell power consumption, etc.
                private static double minVehicleAlternatorVoltage = 13.0; // VOLTS DC // Minimal voltage generated by vehicle's alternator
                private static double maxVehicleAlternatorVoltage = 14.5; // VOLTS DC // Maximal voltage generated by vehicle's alternator
                private static int currentLimit = 60; // AMPERES // Maximal system current usage limit for the cell the Cell
                private static AnalogIn cAV = new AnalogIn((AnalogIn.Pin)FEZ_Pin.AnalogIn.An2);
                private static int voltage;
                
                public static void VoltageMonitoring_Thread()
                {

                    // Voltage sensing logic
                    cAV.SetLinearScale(0, 3300);
                    voltage = cAV.Read();
                
                    currentVehicleAlternatorVoltage = (voltage / 1000);

                    if (currentVehicleAlternatorVoltage >= minVehicleAlternatorVoltage && currentVehicleAlternatorVoltage <= maxVehicleAlternatorVoltage)
                    {
                        Debug.Print("Time Stamp: " + DateTime.Now);
                        Debug.Print("Current Power Line voltage is " + (voltage / 1000).ToString() + "." + (voltage % 1000).ToString() + " VDC.\n\r");
                    }
                    else if (currentVehicleAlternatorVoltage > maxVehicleAlternatorVoltage)
                    {
                        //   PWMDutyCycle = PWMDutyCycle; 
                    }
                    else if (currentVehicleAlternatorVoltage < 0.1)
                    {
                        Debug.Print("Time Stamp: " + DateTime.Now);
                        Debug.Print("Sensor connection to the Cell's Power Source is Interrupted.\nHHO Generator is shut off until the connection is restored.\n\r");
                        //  PWMDutyCycle = 0;
                    }
                    else
                    {
                        Debug.Print("Time Stamp: " + DateTime.Now);
                        Debug.Print("Currently the Vehicle does not generate needed voltage to start the Cell.\n" +
                            "Current Power Line voltage is " + (voltage / 1000).ToString() + "." + (voltage % 1000).ToString() + " VDC.\n" +
                            "The Engine is not running or Alternator does not generate minimal required voltage of " + minVehicleAlternatorVoltage + "VDC.\n\r");
                        //  PWMDutyCycle = 0;
                    }
                }

            }

          
        }

        private static void SD_AutoMount_Thread()
        { 
            // Start auto mount loop
            SDAutoMount.Mount.SDMount();

            Thread.Sleep(Timeout.Infinite);
        } // Automounting SD card

        private static void USB_AutoMount_Thread()
        {

            // Start auto mount loop
            USBAutoMount.Mount.USBMount();

            Thread.Sleep(Timeout.Infinite);

        } // Automounting USB Mass Storage
    }
}


#5

Is it big? Can you just post here?


#6

Yes, try to shrink down your code to something that shows the problem but easy to understand/debug.


#7

Congrats, you have created a recursive property! Check your property name and the variable name you try to return.

You must create a private variable of the same type as your property and get / set that variable through your property.


#8

Wouter Huysentruit,
can you point out my error please? I am quiet new to C#.


#9


private static byte _pwmDuty = 0;
 
public static byte PWMDuty  
{
   get { return _pwmDuty; }
   set { _pwmDuty = value; }
}


On a side note. This was probably pointed out to you by VS compiler. Keep your attention on warnings window and try to have 0 warnings there. :wink:


#10

Thanks a lot, guys!
I see my error here. I looped my property.

Can you tell me how my code looks? Should I optimize anything?


#11

Thank you architect, idd warnings == errors!

Well, when ‘getting’ the value from your property, you were asking to get the value from the property that should get the value from the property, and so on… until stack overflow.

The same was true when setting the property.


#12

deleted.


#13

You are also missing Thread.Sleep(Thread.Infinite) in your void Main(). The app is going to quit without it.


#14

Thank you for the comment.
The app actually does not quit, because some of my threads have the infinite sleep time out.

Bellow I post my restructured code since last post. I modified a few things and now it works smoothly. Any comments on improvement of my code are very welcome!

using System;
using System.Collections;
using System.Threading;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT;
using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

// my classes
using DS18B20;
using BMP085_SENSOR;

namespace FEZ_DOMINO_HHO    
{
    public class Program
    {
        public static void Main()
        {
            Thread RTC = new Thread(RTC_init);
            RTC.Start();

            Thread DS18B20_Monitor = new Thread(DS18B20_Thread);
            DS18B20_Monitor.Start();
            
            Thread BMP085_Monitor = new Thread(BMP085_Thread);
            BMP085_Monitor.Start();
            
            Thread Battery_Monitor = new Thread(BatteryMonitor.Fixed.BatteryVDC_Thread);
            Battery_Monitor.Start();

            Thread.Sleep(3000); // Wait for DS18B20 sensor to initialize in order to set right values
            Thread PWM_Monitor = new Thread(PWM_Control_Thread);
            PWM_Monitor.Start();

            Thread.Sleep(1000); // Wait for DS18B20 & BMP085 sensors to initialize, and then PWM_Control_Thread to start
            Thread PWM_SETTLE_Monitor = new Thread(PWM_SETTLED_Thread);
            PWM_SETTLE_Monitor.Start();

            Thread SD_AutoMount_Monitor = new Thread(SD_AutoMount_Thread);
            SD_AutoMount_Monitor.Start();

            Thread USB_AutoMount_Monitor = new Thread(USB_AutoMount_Thread);
            USB_AutoMount_Monitor.Start();

            Thread LEDPOWER_Monitor = new Thread(LEDPOWER_Thread);
            LEDPOWER_Monitor.Start();


            Debug.GC(true); // Enable Garbage Collector
            Debug.EnableGCMessages(true); // Enable / Disable Garbage Collector Messages

            Thread.Sleep(Timeout.Infinite);
        }


        private static void RTC_init()
        {
            // To keep track of time, 
            // setting it at the beginning of application from the RTC.
            // If it was NOT set before and currently running using 
            // the battery (not exhausted), set it to a fixed time.
            DateTime deviceDateTime = RealTimeClock.GetTime();
            Debug.Print("Getting device's RTC:\t" + deviceDateTime.ToString());
            int rtcFirmwareTime = deviceDateTime.GetHashCode();
            // Debug.Print(rtcYear.ToString()); // getting a printout of default hash code for firmware set time and date.
            if (rtcFirmwareTime == 1243046445) // comparing with hash code of default firmware set time.
                RealTimeClock.SetTime(new DateTime(2011, 01, 18, 12, 0, 0, 0)); // year, month, day, hour, minute, second, millisecond
            Utility.SetLocalTime(RealTimeClock.GetTime());
            deviceDateTime = RealTimeClock.GetTime();
            Debug.Print("Retrieving reset RTC:\t" + deviceDateTime.ToString() + "\r\n");
            /// End setting up date and time

            bool ShowUptime = true; // calculate uptime?
            int UpdateUptimeLoop = 60000; // every ms. 

            System.TimeSpan uptimeCount;
            DateTime startupTime = RealTimeClock.GetTime(); // defining startup time variable
            if (ShowUptime == true)
            {
                while (true)
                {
                    uptimeCount = DateTime.Now.Subtract(startupTime); // Counting System Uptime and sending out to Debug Print
                    Debug.Print("System Uptime: " + uptimeCount.Hours.ToString() + " hours " + uptimeCount.Minutes.ToString() + " minutes\r\n"); // Counting System Uptime and sending out to Debug Print 
                    Thread.Sleep(UpdateUptimeLoop);
                }
            }
        } // Initializing system RTC

        public class PWM_data
        {
            
            public class Fixed
            {
                // Initializing PWM values 

                // max frequency for Power Mosfets:
                // 55V/52A - IRLI3705N Mosfet - typical: 4000 KHz,
                // 125A/24V - NTB125N02R / NTP125N02R Mosfet - max delay: 5494 KHz, typical: 10200 KHz.            

                // DO NOT RAISE FREQUENCY ON USBizi CHIPSETS OVER 5 Mhz! It is BUGGY! WHAT IS SAFE MAX PWM FREQ THAT CAN BE USED?
                // Can set up to 1000000 Hz with set duty cycles of 100, 80, 70, 60, 50, 40, 30, 20%. Does not work on 90% cycle.

               
                public const bool PWMMultiFreq = true;  // Turn on multi frequency mode on (true) / off (false)
                public const int PWMfrequency = 23254;  // Frequency in Hz. Possible resonance freq. of water - 42KHz, 454.177 KHz or 2.5 GHz
                                                        // Frequency ranges to try to find best HHO production freq range
                                                        // 100 KHz to 120 KHz 
                                                        // 40 KHz to 44 KHz 
                                                        // 20 KHz to 24 KHz 
                                                        // 9 KHz to 12 KHz 
                                                        // 23.254 Khz / 46.508 khZ - seen at http://hho-blog.info/blog/post/LCD-screen-for-HHO-system.aspx
                                                        // 21.4 & 42.8 mixed Frequency ???

                public const int PWMfrequency2 = PWMfrequency * 2; // Frequency in Hz. Possible resonance freq. of water - 42KHz, 454.177 KHz or 2.5 GHz
                public const int PWMToggleDelay = 2000; // PWM frequencies toggle delay in ms
                public const byte DutyDivider = 2; // Min divider - 2, max divider - 8
                    // PWM Duty Cycle scale down divider. For future use with AMPERAGE MONITOR / ADJUSTER
                public static byte[] DutyTrigger = new byte[8] { 80, 100, 150, 200, 100, 150, 100, 80 }; // pwm duty cycle limits.
                    // Multiply by 2, then it is divided by DutyDivider TO GET BETTER DOWNSCALING ON THE GO.
                public static byte[] DutyLimit = new byte[8] { DutyTrigger[0] /= DutyDivider, DutyTrigger[1] /= DutyDivider, DutyTrigger[2] /= DutyDivider,
                    DutyTrigger[3] /= DutyDivider, DutyTrigger[4] /= DutyDivider, DutyTrigger[5] /= DutyDivider, DutyTrigger[6] /= DutyDivider, DutyTrigger[7] /= DutyDivider };
                
                public static PWM pwm = new PWM((PWM.Pin)FEZ_Pin.PWM.Di8); // PWM on Digital Pin 8
                public static PWM pwm_duplicate = new PWM((PWM.Pin)FEZ_Pin.PWM.Di9); // Duplicate PWM on Digital Pin 9
                public static int[] TempLimit = new int[8] { -20, -10, 0, 35, 45, 50, 53, 55 }; // temperature limits in Celsius for PWM
                public static int[] CoolantFanTemp = new int[2] { 40, 35 }; // temperature limits in Celsius for PWM

            }

            public static byte _PWMDuty = 0;
            public static byte PWMDuty  // Share PWMDuty value across my threads.
            {
                get { return _PWMDuty; }
                set { _PWMDuty = value; }
            }

            public static byte _PWMDutySettled = 0;
            public static byte PWMDutySettled  // Settled PWMDuty by Battery_Monitor.
            {
                get { return _PWMDutySettled; }
                set { _PWMDutySettled = value; }
            }

        } // Shareable PWM data classes

        public static void PWM_Control_Thread()
        {
            bool ignore = true; // Should we check Power Line State

            while (true)
            {
                if (ignore != true)
                {
                    if (BatteryMonitor.currentVehicleAlternatorVoltage >= BatteryMonitor.Fixed.minVehicleAlternatorVoltage && BatteryMonitor.currentVehicleAlternatorVoltage <= BatteryMonitor.Fixed.maxVehicleAlternatorVoltage)
                    {
                        Debug.Print("Time Stamp: " + DateTime.Now);
                        Debug.Print("Current Power Line voltage is " + (BatteryMonitor.currentVehicleAlternatorVoltage).ToString() + " VDC.");
                        PWM_data.PWMDutySettled = PWM_data.PWMDuty;
                    }
                    else if (BatteryMonitor.currentVehicleAlternatorVoltage > BatteryMonitor.Fixed.maxVehicleAlternatorVoltage)
                    {
                        Debug.Print("Time Stamp: " + DateTime.Now);
                        Debug.Print("Current Power Line voltage is " + (BatteryMonitor.currentVehicleAlternatorVoltage).ToString() + " VDC.\n" +
                            "Your Alternator's Voltage boutput is over safe threshold limit of " + BatteryMonitor.Fixed.maxVehicleAlternatorVoltage + "VDC.\n\r");
                        PWM_data.PWMDutySettled = 0;
                    }
                    else if (BatteryMonitor.currentVehicleAlternatorVoltage < 0.1)
                    {
                        Debug.Print("Time Stamp: " + DateTime.Now);
                        Debug.Print("Sensor connection to the Cell's Power Source is Interrupted.\nHHO Generator is shut off until the connection is restored.\n" +
                             "Set Duty Cycle: " + PWM_data.PWMDuty + "%. Settled Duty Cycle: " + PWM_data.PWMDutySettled + "%.\n\r");
                        PWM_data.PWMDutySettled = 0;
                    }
                    else
                    {
                        Debug.Print("Time Stamp: " + DateTime.Now);
                        Debug.Print("Currently the Vehicle does not generate needed voltage to start the Cell.\n" +
                            "Current Power Line voltage is " + (BatteryMonitor.currentVehicleAlternatorVoltage).ToString() + " VDC.\n" +
                            "The Engine is not running or Alternator does not generate minimal required voltage of " + BatteryMonitor.Fixed.minVehicleAlternatorVoltage + "VDC.\n\r");
                        PWM_data.PWMDutySettled = 0;
                    }
                    Thread.Sleep(2000); // how often to update PWM state?
                }
                else
                {
                    PWM_data.PWMDutySettled = PWM_data.PWMDuty;
                    Thread.Sleep(2000);  // how often to update PWM state?
                }
            }
        }

        public static void PWM_SETTLED_Thread()
        {
            byte localDuty;
            byte localDutySettled;
            byte localDutyTemp = 0;
            byte localDutySettledTemp = 0;
    
            if (PWM_data.Fixed.PWMMultiFreq == true)
            {
                
                while (true)
                {
                    localDuty = PWM_data.PWMDuty;
                    localDutySettled = PWM_data.PWMDutySettled;

                    if (localDutySettledTemp != localDutySettled || localDutyTemp != localDuty )
                    {
                        Debug.Print("Dual PWM Frequency: " + (PWM_data.Fixed.PWMfrequency) + "/" + (PWM_data.Fixed.PWMfrequency2) + " Hz. Frequency rotation every " + (PWM_data.Fixed.PWMToggleDelay / 1000.00) + " sec.\n" +
                        "Set Duty Cycle: " + localDuty + "%. Settled Duty Cycle: " + localDutySettled + "%.\n\r");

                        localDutyTemp = localDuty;
                        localDutySettledTemp = localDutySettled;

                        if (localDutySettled != 0)
                            localDutySettled -= 1;
                    }

                    PWM_data.Fixed.pwm.Set(PWM_data.Fixed.PWMfrequency, localDutySettled); // #1 FREQ. Output to Power Mosfets
                    PWM_data.Fixed.pwm_duplicate.Set(PWM_data.Fixed.PWMfrequency, localDutySettled); // Duplicate output to control the PWM signal
                    Thread.Sleep(PWM_data.Fixed.PWMToggleDelay);

                    PWM_data.Fixed.pwm.Set(PWM_data.Fixed.PWMfrequency2, localDutySettled); // #2 FREQ. Output to Power Mosfets
                    PWM_data.Fixed.pwm_duplicate.Set(PWM_data.Fixed.PWMfrequency2, localDutySettled); // Duplicate output to control the PWM signal
                    Thread.Sleep(PWM_data.Fixed.PWMToggleDelay);
                }
            }
            else
            {
                while (true)
                {
                    localDuty = PWM_data.PWMDuty;
                    localDutySettled = PWM_data.PWMDutySettled;

                    if (localDutySettledTemp != localDutySettled || localDutyTemp != localDuty)
                    {
                        Debug.Print("Single PWM Frequency: " + (PWM_data.Fixed.PWMfrequency) + " Hz.\n" +
                        "Set Duty Cycle: " + localDuty + "%. Settled Duty Cycle: " + localDutySettled + "%.\n\r");

                        localDutyTemp = localDuty;
                        localDutySettledTemp = localDutySettled;

                        if (localDutySettled != 0)
                            localDutySettled -= 1;
                    }

                    PWM_data.Fixed.pwm.Set(PWM_data.Fixed.PWMfrequency, localDutySettled); // #1 FREQ. Output to Power Mosfets
                    PWM_data.Fixed.pwm_duplicate.Set(PWM_data.Fixed.PWMfrequency, localDutySettled); // Duplicate output to control the PWM signal
                    Thread.Sleep(PWM_data.Fixed.PWMToggleDelay);
                }  
            }
        }

        public static void LEDPOWER_Thread()
        {
              OutputPort LED;
            LED = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, true);
            while (true)
            {
                LED.Write(!LED.Read());
                Thread.Sleep(500);
            }
        } // Power LED

        public static void DS18B20_Thread() // Monitoring DS18B20 sensor data and adjusting PWM
        {
            var w = DS18B20.DS18B20.cTemp ? new ThermometerWatcher(new Thermometer((Cpu.Pin)FEZ_Pin.Digital.An1), Thermometer.DataToC) :
                            new ThermometerWatcher(new Thermometer((Cpu.Pin)FEZ_Pin.Digital.An1), Thermometer.DataToF);

            // defining Output Port for switching on/of Relay Mosfet for Coolant Fan
            OutputPort CoolantLineFan;
            CoolantLineFan = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di13, false);

            int i;

            w.TemperatureChanged += (from, to) =>
            {
                // Adjusting Duty Cycle of PWM according to Temperature Changes

                double double_temp = DS18B20.DS18B20.cTemp ? to : ((to - 32) / 1.8);

                for (i = PWM_data.Fixed.DutyLimit.Length; i > 0; i--)
                {
                    i -= 1;
                    if (double_temp <= PWM_data.Fixed.TempLimit[i])
                    {
                        PWM_data.PWMDuty = PWM_data.Fixed.DutyLimit[i];
                    }
                    else if (double_temp >= PWM_data.Fixed.TempLimit[PWM_data.Fixed.TempLimit.Length-1])
                    {
                        PWM_data.PWMDuty = 0; Debug.Print("HHO CELL IS OVERHEATED. TEMPERATURE IS " + to + DS18B20.DS18B20.TempSymbol + ". TEMPORARY SHUTOFF."); 
                    }
                }   
                

                if (double_temp >=PWM_data.Fixed.CoolantFanTemp[0])
                {
                    if (CoolantLineFan.Read())
                    return;
                    else
                    {
                    CoolantLineFan.Write(true);
                    Debug.Print("Cell Temperature is " + to + DS18B20.DS18B20.TempSymbol + ". Switching ON Coolant Fan.");
                    }
                } else
                {
                    if (CoolantLineFan.Read() == true)
                    {
                        if (double_temp <=PWM_data.Fixed.CoolantFanTemp[1])
                        {
                            CoolantLineFan.Write(false);
                            Debug.Print("Cell Temperature dropped to " + to + DS18B20.DS18B20.TempSymbol + ". Switching OFF Coolant Fan.");
                        }
                    }
                }


                Debug.Print("Time Stamp: " + DateTime.Now);
                Debug.Print("DS18B20 Sensor: Temperature = " + to + DS18B20.DS18B20.TempSymbol + "\r\n");
               
                Thread.Sleep(500);
            };

            w.Start();

            Thread.Sleep(Timeout.Infinite);
        }

        private static void BMP085_Thread()
        {

            //Use maximum precision (and maximum power use), auto acquire every 10s
            BMP085_SENSOR.BMP085 MyBMP085 = new BMP085_SENSOR.BMP085(Oversampling.Level3, 10);
            if (!MyBMP085.Ok) throw new Exception("Unable to access the BMP085 sensor");
            Thread.Sleep(50); // Wait for Sensor to make measurement

            OutputPort CompartmentFan;
            CompartmentFan = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.Di12, false);
            int[] CompartmentTemp = new int[2] { 30, 25 }; // Compartment threshold temperature points in C.

            int alt = 0;
            float tem = 0;
            while (true)
            {
                if (MyBMP085.Ok)    // sensor values reading
                {
                    if (MyBMP085.Altitude != alt | MyBMP085.Temperature != tem)
                    {
                        if (DS18B20.DS18B20.cTemp)
                        {
                            Debug.Print("Time Stamp: " + DateTime.Now);
                            Debug.Print("BMP085 Sensor: Temperature = " + MyBMP085.Temperature.ToString("F1") + "° C, " +
                            "Pressure = " + (MyBMP085.Pressure) + " Pa, " + "Altitude = " + ((MyBMP085.Altitude) + " meters.\r\n"));

                            double MyBMP085_Temp = MyBMP085.Temperature;
                            if (MyBMP085_Temp >= CompartmentTemp[0])
                            {
                                if (CompartmentFan.Read())
                                    return;
                                else
                                {
                                    CompartmentFan.Write(true);
                                    Debug.Print("Compartment Temperature is " + MyBMP085.Temperature.ToString("F1") + DS18B20.DS18B20.TempSymbol + ". Switching ON Cooling Fan.");
                                }
                            }
                            else
                            {
                                if (CompartmentFan.Read() == true)
                                {
                                    if (MyBMP085_Temp <= CompartmentTemp[1])
                                    {
                                        CompartmentFan.Write(false);
                                        Debug.Print("Compartment Temperature dropped to " + MyBMP085.Temperature.ToString("F1") + DS18B20.DS18B20.TempSymbol + ". Switching OFF Cooling Fan.");
                                    }
                                }
                            }

                        }
                        else
                        {
                            Debug.Print("Time Stamp: " + DateTime.Now);
                            Debug.Print("BMP085 Sensor: Temperature = " + (32 + (9 / 5) * (MyBMP085.Temperature / 16)).ToString("F1") + "° F, " +
                            "Barometric Pressure = " + (MyBMP085.Pressure) + " Pa, " +
                            "Altitude = " + (((MyBMP085.Altitude) * 3.2808) + " feet.\r\n"));

                            double MyBMP085_Temp = MyBMP085.Temperature;
                            if (MyBMP085_Temp >= CompartmentTemp[0])
                            {
                                if (CompartmentFan.Read())
                                    return;
                                else
                                {
                                    CompartmentFan.Write(true);
                                    Debug.Print("Compartment Temperature is " + MyBMP085.Temperature.ToString("F1") + DS18B20.DS18B20.TempSymbol + ". Switching ON Cooling Fan.");
                                }
                            }
                            else
                            {
                                if (CompartmentFan.Read() == true)
                                {
                                    if (MyBMP085_Temp <= CompartmentTemp[1])
                                    {
                                        CompartmentFan.Write(false);
                                        Debug.Print("Compartment Temperature dropped to " + MyBMP085.Temperature.ToString("F1") + DS18B20.DS18B20.TempSymbol + ". Switching OFF Cooling Fan.");
                                    }
                                }
                            }

                        }
                        alt = MyBMP085.Altitude;
                        tem = MyBMP085.Temperature;
                    }
                }
                else Debug.Print("Unable to access the BMP085 sensor !");
                Thread.Sleep(5000);
            }
        } // Monitor Ambient Temperature / Pressure sensor (BMP085)

        public static class BatteryMonitor // Monitor Vehicle's Voltage output
        {
            public static float _currentVehicleAlternatorVoltage;
            public static float currentVehicleAlternatorVoltage  // Share Voltage value across my threads.
            {
                get { return _currentVehicleAlternatorVoltage; }
                set { _currentVehicleAlternatorVoltage = value; }
            }

            public static float _voltage;
            public static float voltage  // Share Voltage value across my threads.
            {
                get { return _voltage; }
                set { _voltage = value; }
            }

            public class Fixed
            {
                // Init values for battery voltage monitoring, Cell power consumption, etc.
                public const float minVehicleAlternatorVoltage = 13.3F; // VOLTS DC // Minimal voltage generated by vehicle's alternator
                public const float maxVehicleAlternatorVoltage = 16.2F; // VOLTS DC // Maximal voltage generated by vehicle's alternator
                private const float VoltageMultiplier = 4.84F;  // Set VoltageMultiplier to fit top 3.3V analog input margin
                                                            // on Analog pin based on Voltage Divider resistors values:
                                                            // 16V scale down to 3.302V uses R1 15K & R2 3.9K
                //private static int currentLimit = 60; // AMPERES // Maximal system current usage limit for the cell the Cell
                private static AnalogIn cAV = new AnalogIn((AnalogIn.Pin)FEZ_Pin.AnalogIn.An2);
             
                public static void BatteryVDC_Thread()
                {
                    // Voltage sensing logic
                    cAV.SetLinearScale(0, 3300);

                    while (true)
                    {
                        voltage = cAV.Read();
                        currentVehicleAlternatorVoltage = ((voltage / 1000) * VoltageMultiplier);
                        Thread.Sleep(3000);
                    }
                }

            }

          
        }

        private static void SD_AutoMount_Thread()
        { 
            // Start auto mount loop
            SDAutoMount.Mount.SDMount();

            Thread.Sleep(Timeout.Infinite);
        } // Automounting SD card

        private static void USB_AutoMount_Thread()
        {

            // Start auto mount loop
            USBAutoMount.Mount.USBMount();

            Thread.Sleep(Timeout.Infinite);

        } // Automounting USB Mass Storage
    }
}