General Advice About How To Learn C#

Greetings! I’m rather verbose in most communications because I believe background knowledge is important.

I am well versed in Matlab.
I have a Cerberus with 2 button modules, a 16x2 Character display, 5 breakout boards, a relay module, a battery pack module
I will eventually have a 0-100 psig pressure transducer that’s powered via 5 volts and outputs 0.5 to 4.5 volts, a solenoid valve that operates on AA cells powered through the relay

  1. Overall Purpose of Program

  2. What I’m currently doing to learn C#

  3. What I’ve figured out so far

  4. A specific problem I’m having related to using a button module

  5. The overall purpose of the program is to surface a “breath hold diver” if they either A. Descend deeper than the pressure source will support, B. Fail to resurface after a predetermined time.

I want to use a button to cycle through options to choose a pressure status of the bottle and the other button to confirm the selection.

Then I want to use the same button to cycle through another set of options, again with the other button to confirm the selection.

After that, I want to start a timer once the pressure transducer senses that the breath hold diver has descended. I want to be able to interrupt this timer and add to it while the diver is under water (until a maximum time.)

If the diver does not surface within the prescribed time, the solenoid valve actuates via the relay to inflate the pressure bladder.

If the diver does surface before the timer goes off, I want the timer count to be suspended.

I then want to give the diver the option to keep the current settings or change them. This whole idea needs to be able to loop.

  1. So far I’ve read all the beginners guides I could find on here and adapted Joystick/LED tutorial for my buttons, relay and character display.

I also bought a book, which is driving me a little crazy since it’s oriented towards app development. It’s still good though.

I watch about 4 hours of C# 101 by 3dbuzz.com.

I’ve been trying to find things that I can understand in the codeshare but a good portion of it is confusing. I understand all the ideas behind the programming and whatnot, the syntax simply eludes me.

  1. I can work my character display to say whatever I want triggered by a button press.

I can work my relay to turn on and off for whatever time period I desire triggered by a button press and not by a button press.

I made a while loop that acts as a timer by putting the thread to sleep and displaying the time elapsed/remaining using integer values that count each loop and I’ve integrated them to print to the character display.

I was able to glean a piece of code from some other forum question to be able to read the voltage of pin 20 from the cerberus board except I had to do it by using the breakoutboard and specifying pin 3 (it already figured out what socket it was using.) I found out by crashing the code that this value needs to be disposed of or things go haywire.

  1. I want to use the button press outside of the method “void programstarted()” but it won’t recognize the command as being valid anywhere but in “void programstarted().” Essentially, I want to make what the button does change based on what class I’m in.

TL;DR, please give me some advice on how I might speed along my learning process. I feel like the button method only being available within the “void programstarted()” method might be unique to gadgeteering. 'Course, I don’t know that for sure since I’ve never done anything else with it.

Welcome!

“how I might speed along my learning process”

I read your statement

I was able to glean a piece of code from some other forum question to be able to read the voltage of pin 20 from the cerberus board except I had to do it by using the breakoutboard and specifying pin 3 (it already figured out what socket it was using.) I found out by crashing the code that this value needs to be disposed of or things go haywire.

From that I would think you are well on your way in the learning process.

As far as “the syntax simply eludes me”

I get hung up on that often and I am not sure of a good way to solve that. I guess it is like most things. You are either good at it or not!

I find that highlighting a word in a code statement and then right click on the word, select ‘Go To Definition’ (F12) will open a portion of your code or bring up a window from metadata.

Such as button.ButtonPressed += button_ButtonPressed;

‘button’ will open up the code Program.generated.cs
’ButtonPressed’ will open up Button [from metadata]

From metadata you can ‘Go To Definition’ (F12) and find additional information.

'I want to use a button to cycle through options to choose a pressure status of the bottle and the other button to confirm the selection.

Then I want to use the same button to cycle through another set of options, again with the other button to confirm the selection.’

A bit difficult (but not impossible) to do with only one button. You would need to count button presses and use a switch or if else statements to go to the methods that perform a particular task. You would be better off using a Keypad KP16 or something like it.

Then again there is Glide that you can use to build a graphic button (menu) display.
Go to codeshare and search menu. Find something that you like and try to implement it into what you need. That would be a good for understanding the syntax from the code (seen below)

Take a bit of code from codeshare or other source that you have hardware for and single-step through the code. Set breakpoints and watches at places you want to study. I find this helps a lot if you want to find out what is really going on with some bit of code. There is a issue with doing that because you may step into a bit of code that requires the source code. All of the open source code is available but you would need to find it which can be a bit difficult.

Then again it all boils down to ‘try and try again’ and it gets easier after each try. However, there is no way to know how in advance how many tries for a particular tasks…

Have FUN and hang in there!

I think you need to thing about your application in a few different areas before you get too hung up on things on the HOW to program this in C#, but just think about the WHAT.

Let me preface all this by saying there are many ways you could approach your entire problem, so things I’ll mention here are not the only way to deal with this…

One of the first things I always tell people is that you need to break the problem down into modular behaviours, you then want to figure out each piece of the puzzle independently, prove you can overcome each one, and the “Full” application is really using the building blocks you’ve already overcome and combining them. Hope that makes sense.

Here’s something you should think about. You have buttons. When you press them, while on the surface, they change the functionality or set points. In Gadgeteer sense, that means you need to use the Button Press events to know that the button was pressed, and then react. Depending on which button was pressed you may change the setting up or down, or you may change whether you’re changing the depth or the time - you should think about how that is going to work, what do you expect the user interaction to be. You will need to track the state of what you’re adjusting, what you’re waiting for, what values you have etc. As part of the change process you’re going to display something appropriate on the display (and since you only have the 16x2 character display, you don’t need the complexity of Glide). So figuring out how to manage the state and settings is a larger task (you might need to think about researching state machines, or you might be able to simply act on global variables) but that’s all driven by the interaction by the button presses. This is certainly achievable with just two buttons and a time-out, but a third button might help (up, down, select).

Another area to think about. You need to detect when the diver starts to descend. To me that seems like you’ll need to be reading the pressure sensors value periodically and checking if the value has changed (within some tolerance). So this task sounds to me like a timer. Say for instance you do that every quarter second, and then when you detect a pressure change you can record the start time. You may want to record those values for later review, think about whether you create a log of these over time - but you’ll certainly need to continue running this timer because your end-state is when they surface again and you’ll need to be continuing to read the value to figure that out.

Another one. Once the state machine says you’re submerging, you need to check whether the current time is longer than the (start dive time plus the selected dive duration). When you hit that threshold, you then need to actuate the bladder. So this could be done one of two ways, either as part of the above pressure sensing timer, or as part of a different timer loop. Perhaps you want to check this 10 times a second, or only once a second, but if you want to check at a different frequency to the pressure sensor timer then you just use it’s own timer.

So once you start to work through a specific task like each of these, figure out how you think the approach will work, and feel free to ask questions about how to achieve each of those pieces separately.

As for your TL;DR: I don’t really understand what you think isn’t right or won’t achieve the outcome you need. ButtonPressed events are awesome.

Thank you for taking the time to respond!

The issues I’m having are as follows

  1. I want to stop a while loop when a button is pressed. The code I have for it is underlined in red.
  2. I want to make an if statement true when a button is pressed. Code for that is underlined in red too.
  3. I want to assign the voltage input from the ADC in socket 3, pin 3 to a variable. It keeps telling me that it can’t be implicitly assigned to every variable I tried. I tried casting it as well, then it says it can’t do it that way either.
  4. I want multiple methods to work on the same variable. I declared those variables in the class before any methods are used. Will that mess anything up?

Here is my code, in full,

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 Gadgeteer.Modules.GHIElectronics;

namespace BreathHoldDiver
{
public partial class Program
{

    int divetimeseconds = 120; // Sets the intial dive time to 120 seconds. I'm not sure if this was placed in the right spot
    int bottlepress = 750; // Sets the intial dive time to 120 seconds. I'm not sure if this was placed in the right spot
    // I want these two variables to be chosen once and simply keep their value (with the option to be changed if the user wants to)
    double maxdepth = 40; // Sets the intial maximum depth to 40 feet

    void ProgramStarted()
    {

        Debug.Print("Program Started!");

        button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
        button.ButtonReleased += new Button.ButtonEventHandler(button_ButtonReleased);
        button2.ButtonPressed += new Button.ButtonEventHandler(button2_ButtonPressed);
        button2.ButtonReleased += new Button.ButtonEventHandler(button2_ButtonReleased);

        while (true)
        {
            Debug.Print("Main Program While Loop Started");
            AcquireBottlePressure();
            AcquireDiveTime();
            if (AreWeDiving() == true)
            {
                Debug.Print("Are We Diving Method returned true, running Surface Diver If");
                SurfaceDiverIf();
            }
        }
    }

    void button_ButtonPressed(Button sender, Button.ButtonState state)
    {
        Debug.Print("Button Pressed");
    }

    void button_ButtonReleased(Button sender, Button.ButtonState state)
    {
        Debug.Print("Button Released");
    }

    void button2_ButtonPressed(Button sender, Button.ButtonState state)
    {
        Debug.Print("Button 2 Pressed");
    }

    void button2_ButtonReleased(Button sender, Button.ButtonState state)
    {
        Debug.Print("Button 2 Released");
    }

    public int AcquireBottlePressure() //The purpose of this method is to cycle through choices for the bottle pressure
    {
        Debug.Print("Acquire Bottle Pressure Method Started");
        char_Display.Clear();
        while (button2_ButtonReleased = true) // The purpose of this while loop is to run until button 2 is pressed
        {
            if (button_ButtonPressed = true) // The purpose of this if statement is to only add to the "bottlepress" integer
            {
                Debug.Print("Bottle Pressure Decremented by 50 PSI");
                bottlepress = bottlepress - 50; // Changes the bottle pressure by 50 psi at a time
                if (bottlepress == 0)
                { bottlepress = 750; } // Prevents setting the bottle pressure to vacuum and allows the user to cycle again starting at 750 psi
            }
            char_Display.Clear();
            char_Display.PrintString("Enter Bott Press");
            char_Display.SetCursor(1, 0);
            char_Display.PrintString(bottlepress + " psi");
        }
        Debug.Print("Acq Bott Press While Loop exited");
        double maxpress=((bottlepress * 0.57377)/10.57377); // Using the Pi * Vi = Pf * Vf equation, the initial volume is 0.57 Liters, final is 10.57 Liters 
        double maxdepth = maxpress / 0.44; // Determines the max depth the rig can go to given the pressure bottle setting
        return bottlepress;
    }

    public int AcquireDiveTime() // The purpose of this method is to cycle through choices until the right amount of time is selected
    {
        Debug.Print("Acquire Dive Time Method Started");
        char_Display.Clear();
        while (button2_ButtonReleased = true) // The purpose of this while loop is similar to the acquire bottle pressure method
        {
            if (button_ButtonPressed = true)
            {
                Debug.Print("Dive Time Decremented");
                divetimeseconds = divetimeseconds - 5; // Decrements the dive time 5 seconds at time
                if (divetimeseconds == 0)
                { divetimeseconds = 120; } // Allows the user to cycle through difference time settings
            }
            char_Display.Clear();
            char_Display.PrintString("Enter Dive Time");
            char_Display.SetCursor(1, 0);
            char_Display.PrintString(divetimeseconds + " psi");
        }
        Debug.Print("Acq Dive Time While Loop Exited");
        return divetimeseconds;
    }

    public Boolean AreWeDiving() // The purpose of this method is to check the status of diving every 100 miliseconds
    {
        Debug.Print("Are We Diving Method Started");
        char_Display.Clear();
        char_Display.PrintString("Monitoring");
        char_Display.SetCursor(1, 0);
        char_Display.PrintString("Dive Status");
        while (true)
        {
            Gadgeteer.Interfaces.AnalogInput analogin = breakout.SetupAnalogInput(GT.Socket.Pin.Three);
            if (analogin > 0.5176)
            {
                char_Display.Clear();
                char_Display.PrintString("DIVE!DIVE!DIVE!");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString("DIVE!DIVE!DIVE!");
                return true;
            }
            if (button2_ButtonPressed = true) // The purpose of this is to allow the user to exit the AreWeDiving 
            { return false; }                        // method and reselect the dive time and bottle pressure
            Thread.Sleep(100);
            analogin.Dispose();
        }
        Debug.Print("Are We Diving While Loop Exited");
    }

    public void SurfaceDiverIf() // The purpose of this method is to surface the diver if time expires or they go too deep
    {
        int preventtoohigh = 0; // Counter to prevent the time from exceeding 120 seconds
        int seconds = divetimeseconds; // Creates a new time tracking variable so the first one can be reused
        while (seconds > 0)
        {
            Gadgeteer.Interfaces.AnalogInput analogin = breakout.SetupAnalogInput(GT.Socket.Pin.Three);
            double voltsdiff = analogin - 0.5; // Pressure Transducer outputs minimum 0.5 volts
            double pressdiff = voltsdiff * 25; // Converts volts to psi (25 psi/volt)
            double presentdepth = pressdiff/0.44; // Converts PSI to depth (0.44 PSI / ft seawater)
            char_Display.Clear();
            char_Display.PrintString(seconds + " seconds");
            char_Display.SetCursor(0, 1);
            char_Display.PrintString(presentdepth + " ft of " + maxdepth);
            Thread.Sleep(1000);
            preventtoohigh++;
            seconds--;
            
            if (preventtoohigh > 120)
            {
                Debug.Print("Maximum time allowed reached");
                SurfaceDiver();
                break;
            }
            
            if (button_ButtonPressed = true)
            {
                Debug.Print("Dive Time Increased By 5 Sec");
                seconds = seconds + 5;
            }

            if (presentdepth>maxdepth)
            {
                Debug.Print("Maximum depth reached");
                SurfaceDiver();
                break;
            }
            analogin.Dispose();
        }
        Debug.Print("Surface Diver If method while loop exited");
        SurfaceDiver();
    }
    void SurfaceDiver()
    {
        Debug.Print("Surface Diver Method Activated");
        relay_X1.TurnOn();
        Thread.Sleep(1000);
        relay_X1.TurnOff();
    }
}

}

Can you edit your post and put a code block around your actual code? Use the upper “101010” button to insert a code wrapper.

Read the why not… post to see why this method (while true loop in ProgramStarted) in a Gadgeteer app is bad… http://blogs.msdn.com/b/net_gadgeteer/archive/2011/12/19/why-not-while-true.aspx

And again, fundamentally you’re thinking about this in a linear, looping fashion. That’s OK and can be done, but Gadgeteer is actually going to work best for you by thinking about timers and events. Fix your code formatting and more help will come…

Like this?

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 Gadgeteer.Modules.GHIElectronics;

namespace BreathHoldDiver
{
public partial class Program
{

int divetimeseconds = 120; // Sets the intial dive time to 120 seconds. I'm not sure if this was placed in the right spot
int bottlepress = 750; // Sets the intial dive time to 120 seconds. I'm not sure if this was placed in the right spot
// I want these two variables to be chosen once and simply keep their value (with the option to be changed if the user wants to)
double maxdepth = 40; // Sets the intial maximum depth to 40 feet

void ProgramStarted()
{

Debug.Print("Program Started!");

button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
button.ButtonReleased += new Button.ButtonEventHandler(button_ButtonReleased);
button2.ButtonPressed += new Button.ButtonEventHandler(button2_ButtonPressed);
button2.ButtonReleased += new Button.ButtonEventHandler(button2_ButtonReleased);

while (true)
{
Debug.Print("Main Program While Loop Started");
AcquireBottlePressure();
AcquireDiveTime();
if (AreWeDiving() == true)
{
Debug.Print("Are We Diving Method returned true, running Surface Diver If");
SurfaceDiverIf();
}
}
}

void button_ButtonPressed(Button sender, Button.ButtonState state)
{
Debug.Print("Button Pressed");
}

void button_ButtonReleased(Button sender, Button.ButtonState state)
{
Debug.Print("Button Released");
}

void button2_ButtonPressed(Button sender, Button.ButtonState state)
{
Debug.Print("Button 2 Pressed");
}

void button2_ButtonReleased(Button sender, Button.ButtonState state)
{
Debug.Print("Button 2 Released");
}

public int AcquireBottlePressure() //The purpose of this method is to cycle through choices for the bottle pressure
{
Debug.Print("Acquire Bottle Pressure Method Started");
char_Display.Clear();
while (button2_ButtonReleased = true) // The purpose of this while loop is to run until button 2 is pressed
{
if (button_ButtonPressed = true) // The purpose of this if statement is to only add to the "bottlepress" integer
{
Debug.Print("Bottle Pressure Decremented by 50 PSI");
bottlepress = bottlepress - 50; // Changes the bottle pressure by 50 psi at a time
if (bottlepress == 0)
{ bottlepress = 750; } // Prevents setting the bottle pressure to vacuum and allows the user to cycle again starting at 750 psi
}
char_Display.Clear();
char_Display.PrintString("Enter Bott Press");
char_Display.SetCursor(1, 0);
char_Display.PrintString(bottlepress + " psi");
}
Debug.Print("Acq Bott Press While Loop exited");
double maxpress=((bottlepress * 0.57377)/10.57377); // Using the Pi * Vi = Pf * Vf equation, the initial volume is 0.57 Liters, final is 10.57 Liters
double maxdepth = maxpress / 0.44; // Determines the max depth the rig can go to given the pressure bottle setting
return bottlepress;
}

public int AcquireDiveTime() // The purpose of this method is to cycle through choices until the right amount of time is selected
{
Debug.Print("Acquire Dive Time Method Started");
char_Display.Clear();
while (button2_ButtonReleased = true) // The purpose of this while loop is similar to the acquire bottle pressure method
{
if (button_ButtonPressed = true)
{
Debug.Print("Dive Time Decremented");
divetimeseconds = divetimeseconds - 5; // Decrements the dive time 5 seconds at time
if (divetimeseconds == 0)
{ divetimeseconds = 120; } // Allows the user to cycle through difference time settings
}
char_Display.Clear();
char_Display.PrintString("Enter Dive Time");
char_Display.SetCursor(1, 0);
char_Display.PrintString(divetimeseconds + " psi");
}
Debug.Print("Acq Dive Time While Loop Exited");
return divetimeseconds;
}

public Boolean AreWeDiving() // The purpose of this method is to check the status of diving every 100 miliseconds
{
Debug.Print("Are We Diving Method Started");
char_Display.Clear();
char_Display.PrintString("Monitoring");
char_Display.SetCursor(1, 0);
char_Display.PrintString("Dive Status");
while (true)
{
Gadgeteer.Interfaces.AnalogInput analogin = breakout.SetupAnalogInput(GT.Socket.Pin.Three);
if (analogin > 0.5176)
{
char_Display.Clear();
char_Display.PrintString("DIVE!DIVE!DIVE!");
char_Display.SetCursor(1, 0);
char_Display.PrintString("DIVE!DIVE!DIVE!");
return true;
}
if (button2_ButtonPressed = true) // The purpose of this is to allow the user to exit the AreWeDiving
{ return false; } // method and reselect the dive time and bottle pressure
Thread.Sleep(100);
analogin.Dispose();
}
Debug.Print("Are We Diving While Loop Exited");
}

public void SurfaceDiverIf() // The purpose of this method is to surface the diver if time expires or they go too deep
{
int preventtoohigh = 0; // Counter to prevent the time from exceeding 120 seconds
int seconds = divetimeseconds; // Creates a new time tracking variable so the first one can be reused
while (seconds > 0)
{
Gadgeteer.Interfaces.AnalogInput analogin = breakout.SetupAnalogInput(GT.Socket.Pin.Three);
double voltsdiff = analogin - 0.5; // Pressure Transducer outputs minimum 0.5 volts
double pressdiff = voltsdiff * 25; // Converts volts to psi (25 psi/volt)
double presentdepth = pressdiff/0.44; // Converts PSI to depth (0.44 PSI / ft seawater)
char_Display.Clear();
char_Display.PrintString(seconds + " seconds");
char_Display.SetCursor(0, 1);
char_Display.PrintString(presentdepth + " ft of " + maxdepth);
Thread.Sleep(1000);
preventtoohigh++;
seconds--;

if (preventtoohigh > 120)
{
Debug.Print("Maximum time allowed reached");
SurfaceDiver();
break;
}

if (button_ButtonPressed = true)
{
Debug.Print("Dive Time Increased By 5 Sec");
seconds = seconds + 5;
}

if (presentdepth>maxdepth)
{
Debug.Print("Maximum depth reached");
SurfaceDiver();
break;
}
analogin.Dispose();
}
Debug.Print("Surface Diver If method while loop exited");
SurfaceDiver();
}
void SurfaceDiver()
{
Debug.Print("Surface Diver Method Activated");
relay_X1.TurnOn();
Thread.Sleep(1000);
relay_X1.TurnOff();
}
}
}

Much more readable - well kinda. Anyway there’s an EDIT option (the little grey pencil icon top right) that you can edit your old post and do the same thing with.

But I notice you still haven’t re-coded around the while-true loop?

I didn’t have a chance to read it until now. Thank you!

I have learnt the ways of the dispatcher!



            /*******************************************************************************************
            Modules added in the Program.gadgeteer designer view are used by typing 
            their name followed by a period, e.g.  button.  or  camera.
            
            Many modules generate useful events. Type +=<tab><tab> to add a handler to an event, e.g.:
                button.ButtonPressed +=<tab><tab>
            
            If you want to do something periodically, use a GT.Timer and handle its Tick event, e.g.:
                GT.Timer timer = new GT.Timer(1000); // every second (1000ms)
                timer.Tick +=<tab><tab>
                timer.Start();
            *******************************************************************************************/

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 Gadgeteer.Modules.GHIElectronics;

namespace BreathHoldDiver
{
    public partial class Program
    {

        int divetimeseconds = 120; // Sets the intial dive time to 120 seconds. I'm not sure if this was placed in the right spot
        int bottlepress = 750; // Sets the intial dive time to 120 seconds. I'm not sure if this was placed in the right spot
        // I want these two variables to be chosen once and simply keep their value (with the option to be changed if the user wants to)
        double maxdepth = 40; // Sets the intial maximum depth to 40 feet

        void ProgramStarted()
        {

            Debug.Print("Program Started!");

            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
            button.ButtonReleased += new Button.ButtonEventHandler(button_ButtonReleased);
            button2.ButtonPressed += new Button.ButtonEventHandler(button2_ButtonPressed);
            button2.ButtonReleased += new Button.ButtonEventHandler(button2_ButtonReleased);

            AfterProgramStarted();
            
        }

        void AfterProgramStarted()
        {
            while (true)
            {
                Debug.Print("AfterProgramStarted While Loop Started");
                AcquireBottlePressure();
                AcquireDiveTime();
                if (AreWeDiving() == true)
                {
                    Debug.Print("Are We Diving Method returned true, running Surface Diver If");
                    SurfaceDiverIf();
                }
            }
        }

        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            Debug.Print("Button Pressed");
        }

        void button_ButtonReleased(Button sender, Button.ButtonState state)
        {
            Debug.Print("Button Released");
        }

        void button2_ButtonPressed(Button sender, Button.ButtonState state)
        {
            Debug.Print("Button 2 Pressed");
        }

        void button2_ButtonReleased(Button sender, Button.ButtonState state)
        {
            Debug.Print("Button 2 Released");
        }

        public int AcquireBottlePressure() //The purpose of this method is to cycle through choices for the bottle pressure
        {
            Debug.Print("Acquire Bottle Pressure Method Started");
            char_Display.Clear();
            while (button2_ButtonReleased = true) // The purpose of this while loop is to run until button 2 is pressed
            {
                if (button_ButtonPressed = true) // The purpose of this if statement is to only add to the "bottlepress" integer
                {
                    Debug.Print("Bottle Pressure Decremented by 50 PSI");
                    bottlepress = bottlepress - 50; // Changes the bottle pressure by 50 psi at a time
                    if (bottlepress == 0)
                    { bottlepress = 750; } // Prevents setting the bottle pressure to vacuum and allows the user to cycle again starting at 750 psi
                }
                char_Display.Clear();
                char_Display.PrintString("Enter Bott Press");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString(bottlepress + " psi");
            }
            Debug.Print("Acq Bott Press While Loop exited");
            double maxpress=((bottlepress * 0.57377)/10.57377); // Using the Pi * Vi = Pf * Vf equation, the initial volume is 0.57 Liters, final is 10.57 Liters
            double maxdepth = maxpress / 0.44; // Determines the max depth the rig can go to given the pressure bottle setting
            return bottlepress;
        }

        public int AcquireDiveTime() // The purpose of this method is to cycle through choices until the right amount of time is selected
        {
            Debug.Print("Acquire Dive Time Method Started");
            char_Display.Clear();
            while (button2_ButtonReleased = true) // The purpose of this while loop is similar to the acquire bottle pressure method
            {
                if (button_ButtonPressed = true)
            {
                Debug.Print("Dive Time Decremented");
                divetimeseconds = divetimeseconds - 5; // Decrements the dive time 5 seconds at time
                if (divetimeseconds == 0)
                { divetimeseconds = 120; } // Allows the user to cycle through difference time settings
                }
                char_Display.Clear();
                char_Display.PrintString("Enter Dive Time");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString(divetimeseconds + " psi");
            }
            Debug.Print("Acq Dive Time While Loop Exited");
        return divetimeseconds;
        }

        public Boolean AreWeDiving() // The purpose of this method is to check the status of diving every 100 miliseconds
        {
            Debug.Print("Are We Diving Method Started");
            char_Display.Clear();
            char_Display.PrintString("Monitoring");
            char_Display.SetCursor(1, 0);
            char_Display.PrintString("Dive Status");
            while (true)
            {
                Gadgeteer.Interfaces.AnalogInput analogin = breakout.SetupAnalogInput(GT.Socket.Pin.Three);
                if (analogin > 0.5176)
                {
                    char_Display.Clear();
                    char_Display.PrintString("DIVE!DIVE!DIVE!");
                    char_Display.SetCursor(1, 0);
                    char_Display.PrintString("DIVE!DIVE!DIVE!");
                    return true;
                }
                if (button2_ButtonPressed = true) // The purpose of this is to allow the user to exit the AreWeDiving
                { return false; } // method and reselect the dive time and bottle pressure
            Thread.Sleep(100);
            analogin.Dispose();
            }
            Debug.Print("Are We Diving While Loop Exited");
        }

        public void SurfaceDiverIf() // The purpose of this method is to surface the diver if time expires or they go too deep
        {
            int preventtoohigh = 0; // Counter to prevent the time from exceeding 120 seconds
            int seconds = divetimeseconds; // Creates a new time tracking variable so the first one can be reused
            while (seconds > 0)
            {
                Gadgeteer.Interfaces.AnalogInput analogin = breakout.SetupAnalogInput(GT.Socket.Pin.Three);
                double voltsdiff = analogin - 0.5; // Pressure Transducer outputs minimum 0.5 volts
                double pressdiff = voltsdiff * 25; // Converts volts to psi (25 psi/volt)
                double presentdepth = pressdiff/0.44; // Converts PSI to depth (0.44 PSI / ft seawater)
                char_Display.Clear();
                char_Display.PrintString(seconds + " seconds");
                char_Display.SetCursor(0, 1);
                char_Display.PrintString(presentdepth + " ft of " + maxdepth);
                Thread.Sleep(1000);
                preventtoohigh++;
                seconds--;

                if (preventtoohigh > 120)
                {
                    Debug.Print("Maximum time allowed reached");
                    SurfaceDiver();
                    break;
                }

                if (button_ButtonPressed = true)
                {
                    Debug.Print("Dive Time Increased By 5 Sec");
                    seconds = seconds + 5;
                }

                if (presentdepth>maxdepth)
                {
                    Debug.Print("Maximum depth reached");
                    SurfaceDiver();
                    break;
                }
                analogin.Dispose();
            }
            Debug.Print("Surface Diver If method while loop exited");
            SurfaceDiver();
        }
    
        void SurfaceDiver()
        {
            Debug.Print("Surface Diver Method Activated");
            relay_X1.TurnOn();
            Thread.Sleep(1000);
            relay_X1.TurnOff();
        }
    }
}

I’m reading the example of good code and there’s some things I don’t understand.

I’ve annotated the code with comments as to what I think is going on.


public partial class Program // Creates a class called program which is accessible to other classes.
// I know partial means it can be split up but I don't know what that means
    {
        GT.Timer ledBlinkTimer = new GT.Timer(1000); //creates an instance of Timer and calls it "ledBlinkTimer."
// The GT.Timer(1000) has an argument of 1000 which means... it gets run every 1 seconds?
 
        void ProgramStarted() // Method used for gadgeteering programs
        {
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);

// Most of this syntax confuses me. I think we're using a statement "ButtonPressed" from
// button but I don't know what button is. I would assume it's a class.
// the "+=" confuses me as well. When creating instances of other classes it's just "= new"
// I'm not sure what an "event handler" is other than it's a way to have a piece of software
// tell the program when something happens rather than checking on it constantly.
// I think I should read up on those things. Suggestions?
// button_ButtonPressed is the name of the code that's run when the button is pressed

            ledBlinkTimer.Tick += new GT.Timer.TickEventHandler(ledBlinkTimer_Tick);

// Based on an earlier part of the code I would think this is not necessary. I thought the 
// ledBlinkTimer() code was run every 1 seconds.

            ledBlinkTimer.Start(); // Starts the ledBlinkTimer running

        }
 
        void ledBlinkTimer_Tick(GT.Timer timer) // method does not return a value, but the
// confusing part is why there's an argument in the parenthesis and why those arguments
// are passed
        {
            if (button.IsLedOn == true) // This logic simply keeps the led on until this code is run.
// It then blinks off for a short time and turns back on
            {
                button.TurnLEDOff();
            }
 
            else
                button.TurnLEDOn();
        }
 
        void button_ButtonPressed(Button sender, Button.ButtonState state)
// After having used these a couple times I understand it well enough to know that if the 
// button is pressed the debug statement runs when the button is pressed.
// What I don't know is what the arguments passed mean or what the other options are
        {
            Debug.Print("Button Pressed");   
        }
    }

Could you explain some of the stuff here or point to something that can explain it? Thank you.

Ok, I will try to answer some of your questions, but please take account of the fact, that I have never learned English :wink:


public partial class Program // Creates a class called program which is accessible to other classes.
// I know partial means it can be split up but I don't know what that means

partial keyword in class definition tells to compiler that class is implemented in more than one source file. It allows to split implementation of class to multiple source files and has no impact on actual functionality. Visual Studio uses this for classes which have some designer - UI forms, in gadgeteer connection schema of motherboard and modules, etc. because part of the source code, which represents layout of form, how modules are connected etc. is maintained by the designer and you should never edit it manually.


    {
        GT.Timer ledBlinkTimer = new GT.Timer(1000); //creates an instance of Timer and calls it "ledBlinkTimer."
// The GT.Timer(1000) has an argument of 1000 which means... it gets run every 1 seconds?

Yes, it’s time in miliseconds, so 1000 stands for 1 second. If you are not sure what some parameter means, check help or manual, classes and methods are well documented.


        void ProgramStarted() // Method used for gadgeteering programs
        {
            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);

// Most of this syntax confuses me. I think we're using a statement "ButtonPressed" from
// button but I don't know what button is. I would assume it's a class.
// the "+=" confuses me as well. When creating instances of other classes it's just "= new"
// I'm not sure what an "event handler" is other than it's a way to have a piece of software
// tell the program when something happens rather than checking on it constantly.
// I think I should read up on those things. Suggestions?
// button_ButtonPressed is the name of the code that's run when the button is pressed

button in this code is instance of class which implements functionality of button module. You can change this name to anything else, say myStartButton, in the designer, just click button module and check its properties. Every module you connect in designer to your motherboard has corresponding instance member in Program class including motherboard itself.

ButtonPressed event is basically list of function pointers inside class of button member. += is just syntactical sugar which allows you to add pointer to your method to this internal list. Such method is then usually called event handler. When you press button on the module, all functions in this list are invoked sequentially. Similarly you can remove previously added handler with operator -=. Also I think that the wrapper around event handler is not necessary and you can simply write:


            button.ButtonPressed += button_ButtonPressed;


            ledBlinkTimer.Tick += new GT.Timer.TickEventHandler(ledBlinkTimer_Tick);

// Based on an earlier part of the code I would think this is not necessary. I thought the 
// ledBlinkTimer() code was run every 1 seconds.

What does it mean ledBlinkTimer() code? ledBlinkTimer is just instance of class which implements the timer, another member of class Program, not code. Once you start the timer, its Tick event list is invoked every 1 second. You must again add your method to this internal list, otherwise nothing in your code will know the timer ticked.


        void ledBlinkTimer_Tick(GT.Timer timer) // method does not return a value, but the
// confusing part is why there's an argument in the parenthesis and why those arguments
// are passed

You can add one event handler to multiple events. Say you have two timers and you register method onTimerTick to both. When the Tick happens, how do you know from which timer, if there is no timer passed back to your handler? In code this could be:


timer1.Tick += onTimerTick;
timer2.Tick += onTimerTick;

void onTimerTick(GT.Timer timer)
{
   if( timer == timer1 )
        doSomethingToHandleTimer1();
   else
        doSomethingToHandleTimer2();
}


        void button_ButtonPressed(Button sender, Button.ButtonState state)
// After having used these a couple times I understand it well enough to know that if the 
// button is pressed the debug statement runs when the button is pressed.
// What I don't know is what the arguments passed mean or what the other options are
        {
            Debug.Print("Button Pressed");   
        }
    }

Similarly as above, the sender argument helps you to identify which button caused the event and state argument offers information about actual state of the button.

2 Likes

Thanks so much for your reply!

I read through the link @ Brett gave me more carefully and finally came up with the following.

I’ve tested it out and it works pretty well.

The exact conversion from voltage to depth will be different once I get my hands on the actual pressure transducer and test it.

 
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 Gadgeteer.Modules.GHIElectronics;

namespace BreathHoldPostTimerChain
{
    public partial class Program
    {
        int selector; //I used an integer to keep track of what each of the two buttons do given the current state of the code

        int bottlepressure = 550; //initial setting

        int divetime = 120; //initial setting

        int divetimeproxy; // because the countdown timer in weAREDiving() changes a value each time it's run, I set up
        // another value to stand in for it and be reset to the selected dive time

        GT.Timer areWeDiving = new GT.Timer(250);
        GT.Timer weAREDiving = new GT.Timer(1000);

        double maxdepth; // variable used to store the calculation of max depth

        int timercount = 0; // variable used to prevent a maximum time from being exceeded

        void ProgramStarted()
        {
            Debug.Print("Program Started");

            button.ButtonPressed += new Button.ButtonEventHandler(button_ButtonPressed);
            button2.ButtonPressed += new Button.ButtonEventHandler(button2_ButtonPressed);

            areWeDiving.Tick += new GT.Timer.TickEventHandler(areWeDiving_Tick);
            weAREDiving.Tick += new GT.Timer.TickEventHandler(weAREDiving_Tick);

            initialstate(); // I noticed I ran the code associated with initialstate() frequently so I made it a method
        }

        void button_ButtonPressed(Button sender, Button.ButtonState state)
        {
            Debug.Print("Button 1 Pressed");
            if (selector==1) // this is where that selector variable mentioned earlier comes in. It allows a single button to function differently
            {
                Debug.Print("Check 1");
                bottlepressure = bottlepressure - 25;
                if (bottlepressure == 0)// allows the bottle to reset to max pressure and avoid vacuum pressures
                { bottlepressure = 550; }
                char_Display.Clear();
                char_Display.PrintString("Pressure set to");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString(bottlepressure + " psi");
            }

            if (selector==2)
            {
                Debug.Print("Check 2");
                divetime = divetime - 5;
                if (divetime == 0)
                { divetime = 120; }
                char_Display.Clear();
                char_Display.PrintString("Dive time set to");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString(divetime + " sec");
            }

            if (selector>4)// allows for button 2 to add time to the timer with a maximum of 120 seconds enforces by the timercount variable
            {
                Debug.Print("Check 3");
                divetimeproxy = divetimeproxy + 5;
            }
        }

        void button2_ButtonPressed(Button sender, Button.ButtonState state)
        {
            Debug.Print("Button 2 Pressed");
            if (selector==1)
            {
                Debug.Print("Check 4");

                double maxpress = (bottlepressure * 0.57377) / 10.57377; //calculates the final pressure after inflation
                maxdepth = ((maxpress - 1) / 0.4444)-(14.696/0.4444); //underestimates final pressure and converts to depth
                
                char_Display.Clear();
                char_Display.PrintString("Maximum Depth is:");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString(maxdepth + "ft");

                Thread.Sleep(3000);

                char_Display.Clear();
                char_Display.PrintString("Dive Time");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString("Set at " + divetime);
            }

            if (selector==2)
            {
                Debug.Print("Check 5");
                char_Display.Clear();
                char_Display.PrintString("Waiting for");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString("Dive");
                divetimeproxy = divetime;
                areWeDiving.Start(); // Starts the diving checker
                //Thread.Sleep(5000); //This line is to simulate waiting to submerge
                //weAREDiving.Start(); //Simulates having gone underwater
            }
            
            if (selector==3) // This if statement is here so that the user can change the bottle pressure and dive time
            {
                Debug.Print("Check 6");
                areWeDiving.Stop();
                Thread.Sleep(1000);
                areWeDiving.Stop();
                initialstate();
            }
            selector++;
            if (selector == 4) //resets the selector to 1 if it was changed from 3 to 4
            { selector = 1; } //allows user to cycle from bottle pressure, dive time and waiting for a dive
        }
            void areWeDiving_Tick(GT.Timer timer)// checks the depth and starts looking for force surface conditions
            {
                Gadgeteer.Interfaces.AnalogInput analogvolts = breakout.SetupAnalogInput(GT.Socket.Pin.Three);
                double analogin = analogvolts.ReadVoltage();
                analogvolts.Dispose();

                if (analogin > 0.58)
                {
                    selector = 5;
                    weAREDiving.Start();
                    areWeDiving.Stop();
                }
            }

            void weAREDiving_Tick(GT.Timer timer) // checks for conditions under which to cause the diver to surface
            {
                areWeDiving.Stop();

                //selector = 5; // Added to make the logic work for the test

                char_Display.Clear();
                char_Display.PrintString(divetimeproxy + " sec remain");

                Gadgeteer.Interfaces.AnalogInput analogvolts = breakout.SetupAnalogInput(GT.Socket.Pin.Three);
                double analogin = analogvolts.ReadVoltage();
                analogvolts.Dispose();
                double currentdepth = (11 * analogin) - 5.5;

                if (analogin < 0.58) //This section of code needs to be removed when testing at a voltage <0.58v
                {   
                    weAREDiving.Stop();
                    selector = 1;
                    initialstate();
                }

                char_Display.SetCursor(1, 0);
                char_Display.PrintString(currentdepth + "ft deep");

                timercount++;

                if (timercount == 120)
                { surfaceDiver(); }

                if (divetimeproxy == 0)
                { surfaceDiver(); }

                if (currentdepth > maxdepth)
                { surfaceDiver(); }

                divetimeproxy--;

            }

            void surfaceDiver()// stops checking surface reasons, energizes relay
            {
                weAREDiving.Stop();
                char_Display.Clear();
                char_Display.PrintString("Uh oh!");
                relay_X1.TurnOn();
                Thread.Sleep(1000); // holds relay on for 1 second
                relay_X1.TurnOff();
                Thread.Sleep(5000); // allows the user to observe time delays from surface instructions to the intial state
                initialstate();
            }

            void initialstate()
            {
                char_Display.Clear();
                char_Display.PrintString("Bottle Pressure");
                char_Display.SetCursor(1, 0);
                char_Display.PrintString("Set at " + bottlepressure);
                timercount = 0;
                selector = 1;
            }

        }

    }

1 Like

you could probably clear up the state logic (but would require documenting the state machine), but it looks much better than it did before, great change !!