I’ve run into a problem in my Standard UI program: a button that used to work like a toggle is now toggling on and off immediately. Is it getting called twice? How am I going to track this error down?
CrestronConsole.PrintLine
Sometimes the easiest form of debugging is what is referred to as “printf statements.” In a C program, you would sprinkle printf
s around the area you think something interesting is happening. You can print out messages to check control flow, show contents of variables, anything to give you a hint at what’s going on. As long as you have a console to print to, this works pretty well to only show the information you care about. Luckily, Crestron gives us CrestronConsole to do just that.
Here’s the bit of code where I’m wondering what’s going on:
Button(PopUpSigs.SettingsPopUp, x =>
{ if (x) TogglePopUp(PopUpSigs.SettingsPopUp); });
All this does is call TogglePopUp if the button is pressed (x
is true). If we modify this slightly, we can print out a message to the console to see when our button gets pressed:
Button(PopUpSigs.SettingsPopUp, x =>
{
if (x)
{
CrestronConsole.PrintLine("--- Toggle ---");
TogglePopUp(PopUpSigs.SettingsPopUp);
}
});
When I push the settings button ONCE, sure enough I see this print to the text console:
--- Toggle ---
--- Toggle ---
How is that button handler getting called twice? We can investigate a bit more about the program execution up to that point by looking at a stack trace. Crestron doesn’t allow you to arbitrarily look at the stack, but throwing and catching an exception allows you to get it as a string:
Button(PopUpSigs.SettingsPopUp, x =>
{
if (x)
{
CrestronConsole.PrintLine("--- Toggle ---");
try
{
throw new Exception();
}
catch (Exception e)
{
CrestronConsole.PrintLine(e.StackTrace);
}
TogglePopUp(PopUpSigs.SettingsPopUp);
}
});
When I push my toggle button now, I see this printed to the console:
--- Toggle ---
at StandardUI.UI.UiManager.<.ctor>b__1(Boolean x)
at StandardUI.UI.UiManager.ButtonPress(UInt32 sig)
at StandardUI.UI.UiManager.SigChange(BasicTriList dev, SigEventArgs args)
...
--- Toggle ---
at StandardUI.UI.UiManager.<.ctor>b__1(Boolean x)
at StandardUI.UI.UiManager.ButtonPress(UInt32 sig)
at StandardUI.UI.UiManager.SigChange(BasicTriList dev, SigEventArgs args)
...
I’ve edited out everything that isn’t part of this program (since I’m 99% sure the bug is in my program and not Crestron’s libraries). It’s interesting that the call path is exactly the same! The exception happened in our anonymous function (named <.ctor>b__1 in this trace), so let’s take a look at the ButtonPress method to see if the bug is there:
private void ButtonPress(uint sig)
{
if (_buttons.ContainsKey(sig))
{
var act = _buttons[sig];
if (act != null)
act(true);
}
}
This looks correct: there’s only one Action delegate call on line 8. Let’s keep going up the stack and see what’s in SigChange:
private void SigChange(BasicTriList dev, SigEventArgs args)
{
if (args.Sig.Type == eSigType.Bool)
{
if (OnPress != null)
OnPress(args.Sig.Number);
}
else
{
if (OnRelease != null)
OnRelease(args.Sig.Number);
}
}
Ah ha! We found the bug! Remember that button presses and releases generate events. When I press the menu button, SigChange is eventually called with args.Sig.Type
set to eSigType.Bool
. You can see this check being made on line 3 above. Button presses are treated as boolean data: press is true
, release is false
. Where I messed up is, I should be checking args.Sig.BoolValue
to see if this was a press or release. As it’s written right now, a press and release will always call the OnPress
delegate!
Let’s rewrite this method so it correctly checks args.Sig.BoolValue
instead and test our toggle behavior again:
private void SigChange(BasicTriList dev, SigEventArgs args)
{
if (args.Sig.Type == eSigType.Bool)
{
if (args.Sig.BoolValue)
{
if (OnPress != null)
OnPress(args.Sig.Number);
}
else
{
if (OnRelease != null)
OnRelease(args.Sig.Number);
}
}
}
It works!
Visual Studio Debugger
We were able to easily find this bug because our call stack wasn’t very deep and the methods we had to check were pretty simple. What if we weren’t sure what was supposed to be in args.Sig
when our event triggered? This is where the full Visual Studio Debugger can come in handy.
Full disclaimer: sometimes I can get the debugger to work and sometimes I can’t even get it to connect. Hopefully this is one of the times I can get it working so I can document the process. But you can see how far simple PrintLine statements got us without having to waste time getting the debugger setup.
First, you need to go to the Tools > Options menu in VS2008. This opens the Options dialog. Selecting Device Tools > Devices from the left menu, you’ll see a list of devices on the right side. Pick CrestronSDK from the dropdown menu, and you should see:

Highlight “CrestronSDK ARM7 Device” in the list and hit Save As… to create a copy. I’ve called mine “CP3” since that’s the device I’m using:

Highlight your new entry and hit Properties… to open the device properties. Next to TCP Connect Transport, hit Configure. Enter in the IP address of your processor:

Keep hitting OK until you’ve backed all the way out from the Options dialog. On your processor there is a command you’ll need to enable. Either use Text Console or another SSH client (like PuTTY) to enter in the command:
SSDEBUGENABLE ON
Then reboot the processor by typing REBOOT
. If you have Toolbox open, you’ll need to close it out before proceeding. The COM server started by Toolbox and Visual Studio do not play well together.
In the Solution Explorer, double-click on ControlSystem.cfg to open it. Click the Address Book icon and pick your control system. The window should refresh with a list of running processes:

Note the process ID (in my example, it’s 179961982). Click the debug icon (it’s got a bug with a green plus on it) to enable debugging mode. At the bottom of the window you’ll see “Debug is enabled.”
Now you can go to the Debug > Attach to Process menu. For Transport, pick Smart Device. Click the Browse button to select your processor. Now in the list below, find the process ID for the program you want to debug. If you click the column headers you can easily sort by ID:

Hit Attach and hopefully the whole thing doesn’t crash!
Breakpoints
Let’s revisit that previous bug using the full debugger this time. I’ve put my cursor at the anonymous method I’m using for my Action delegate. Right-click and select Breakpoint > Insert Breakpoint. Now the window will show that a breakpoint is there:

If I go back to my panel and hit the menu button again, this breakpoint should stop the program at that exact moment:

We can inspect any local variable to see what’s going on:

We can also look at the call stack and check every previous frame to see what happened there. For example, if we load the stack frame for the SigChange event handler:

We can see what args.Sig
contained:

And this is where we’d see the breakpoint trigger correctly when Sig.BoolValue = true
, but we would also see it trigger when Sig.BoolValue = false
and lead us back to the same bug in the program.
The Visual Studio Debugger is extremely powerful. It’s just a bit of a challenge getting it to cooperate with Crestron equipment. The best advice I read online says:
- Make absolutely sure the loading program is the same as what’s compiled on your machine (make sure to use Debug > SIMPL# > Upload before trying to debug).
- If you’re debugging a processor that has a Control Subnet, you can only connect via the Control Subnet. My CP3 only has a single LAN connection.
- If Visual Studio won’t load debugging symbols, try exiting out of it and restarting it, sometimes it just gets hung up for some reason.
There you go! Hope you find this useful if you ever need to rely on the Visual Studio debugger, but I’ll probably stick with PrintLine statements when I can get away with it.
I Try Use This Debug S# It Also Can Do,Perfect!!
Can U Share 4 Series Debug?
LikeLike
For 4 series, take a look at https://support.crestron.com/app/answers/detail/a_id/1000637
LikeLiked by 1 person
Now that I have access to a CP4N, I’d like to give https://www.jetbrains.com/rider/ a chance.
LikeLike