Dashboard-Driver Station Help Please

I need help getting the dashboard on the driver station to show me what the robot is doing. I tried to get the dashboard working last year on C++ to no avail, and had hoped that FIRST had got it functioning this year, or at least made it easy that those of us still learning C++ can figure it out.

I have started from the SimpleTemplate code, and the only change I made was to goto TankDrive. I have added the DashboardDataFormat.h and DashboardDataFormat.cpp from the DashboardDataExample and I am posting the code in myRobot.cpp. The robot works fine, it drives around and is definitely talking to the driver station and everything is updated, but the dashboard will not reflect what the PWM’s are doing.

Can someone enlighten me on what I am doing wrong. I expect with this code the way it is to see the PWM sliders on the driver station to move up and down as the joysticks are moved, but it doesn’t do that. I have not included the sendVisionData because we aren’t using the camera. Am I misunderstanding how the dashboard is supposed to work, is it not supposed to update the charts with the PWM positions?

Help me please with this code:

#include "WPILib.h"
#include "DashboardDataFormat.h"

/**
 * This is a demo program showing the use of the RobotBase class.
 * The SimpleRobot class is the base of a robot application that will automatically call your
 * Autonomous and OperatorControl methods at the right time as controlled by the switches on
 * the driver station or the field controls.
 */ 
class RobotDemo : public SimpleRobot
{
	RobotDrive myRobot; // robot drive system
	Joystick leftstick; // left joystick
	Joystick rightstick; //right joystick

public:
	RobotDemo(void):
		myRobot(1, 3, 2, 4),	// PWMs for drive
				//1,3 - Left Motors
				//2,4 - Right Motors
		leftstick(1),		// Left Joystick.
		rightstick(2)		// Right Joystick.
	{
		myRobot.SetInvertedMotor(RobotDrive::kFrontLeftMotor, false);
		myRobot.SetInvertedMotor(RobotDrive::kFrontRightMotor, false);
		myRobot.SetInvertedMotor(RobotDrive::kRearLeftMotor, false);
		myRobot.SetInvertedMotor(RobotDrive::kRearRightMotor, false);
		GetWatchdog().SetExpiration(0.1);
	}

	/**
	 * Drive left & right motors for 2 seconds then stop
	 */
	void Autonomous(void)
	{
		GetWatchdog().SetEnabled(false);
		myRobot.Drive(0.5, 0.0); 	// drive forwards half speed
		Wait(2.0); 				//    for 2 seconds
		myRobot.Drive(0.0, 0.0); 	// stop robot
	}

	/**
	 * Runs the motors with tank steering. 
	 */
	void OperatorControl(void)
	{
		GetWatchdog().SetEnabled(true);
		while (IsOperatorControl())
		{
			GetWatchdog().Feed();
			myRobot.TankDrive(leftstick, rightstick); // drive
			sendIOPortData();
			Wait(0.005);				// wait for a motor update time
		}
	}
};

START_ROBOT_CLASS(RobotDemo);

The default DashboardDataFormat.cpp file has a bunch of fake values plugged in for the PWMs and Analog inputs, with the real stuff commented out. Not sure why they did this…

Thank you auk, I see the comments you referred to, and can figure out which lines I should replace the comments with, except a couple of the lines look like they might be doing something I don’t understand.

Are there any examples that do work? (2010 Vision Demo isn’t) Can someone point me to the document that details what the format that the dashboard is expecting so I know what I need to fill it. I’m gonna try the changes I just made, on the robot tonight, but don’t be surprised if you find me asking for a working Dashboard example if someone is feeling gracious so I can see what I need to do to get my driver station updating with robot information.


This is just general venting, you can skip this unless you want to commiserate or as the students today say: tl,dr

So FIRST once again purposely torpedos the rookies and those just starting to learn, which isn’t very graciously professional if you ask me. Not exactly setting a good example I think. I suppose now that I know that they are false advertising and are not actually providing working examples to learn from, I will be more careful. As a consulation at least they only commented out the code that makes it work.

I suppose their torpedos are also in the 2010 Vision Demo as well which would explain why I could not get it to work either. I keep getting an error on the driver station about a watchdog not being fed. With the Vision Demo I just threw it away since I couldn’t get it to work, eventhough I put the GetWatchDog().Feed(); in the teleoperated section and it still didn’t work. I just assumed that like the rest of the coding and updating that something was broken and that a new example would be coming out soon. I spent the last 3 days just trying to get the systems to talk together because of all the errors in the manual and getting all the proper updates talking.

It’s not like the game and building a robot isn’t difficult enough without maliciously providing red herrings and wrong turns. I understand showing the students how harsh the real world is, but I expect the simple examples to be actually functional, or at least for FIRST not to say that they have sample functioning code for you to learn on. What happened to the good old days of FIRST just 2 years ago, when a default code was provided that would at least have your robot work out of the box if you put your PWM’s to the right places?

You mad?

http://www.youtube.com/watch?v=CnhUYWbW3jQ around ~6:00

Mad is a good word, although I would say I am unusually annoyed. I have found the internet forum to be a good pressure relief for when stupid stuff isn’t working out, which is why I prefaced my vent above. :slight_smile:

That way folks having the same issue can see they aren’t the only ones having their problem.

It honestly never occured to me to read and check every line of the code they said worked and used as an example. I just assumed that I was messing up when calling it in my code. I wonder if I should have to check every line of the WPIlib souce code as well? Where does that mentality stop?

The lack of functionality in the examples is rather annoying. If you’re still having watchdog issues. Try calling GetWatchdog().SetEnabled(false) to shut it off completely. This should stop the issues (with the watchdog at least).

The Watchdog issues may be due to the System Watchdog which no user code affects. There is a Driver station efficency issue that NI has mentioned in other threads that they are working on correcting. When communication between the DS and the cRIO lags too much, the System Watchdog error message comes up.

The vision example is obviously broken with respect to the Watchdog usage. It clearly enables the watchdog, sets expiration to some time (let’s say t seconds, since I don’t have it in front of me) and then proceeds to sleep significantly longer than that expiration (t + x).
You will always get a watchdog not fed at the start of this example if left unmodified.

I do understand your frustration with the quality of the examples.

Ok I am desperate now, I just tried uncommenting the code in the dashboard format and I still don’t get anything on my driver station dashboard. Can someone please help me.

I have tried the same code that you saw in the first post above, and I also tried this code for my myRobot.cpp.

#include "WPILib.h"
#include "DashboardDataFormat.h"

/**
 * This is a demo program showing the use of the RobotBase class.
 * The SimpleRobot class is the base of a robot application that will automatically call your
 * Autonomous and OperatorControl methods at the right time as controlled by the switches on
 * the driver station or the field controls.
 */ 
class RobotDemo : public SimpleRobot
{
	RobotDrive myRobot; // robot drive system
	Joystick leftstick; // left joystick
	Joystick rightstick; //right joystick
	DashboardDataFormat DataPlease;
	
public:
	RobotDemo(void):
		myRobot(1, 3, 2, 4),	// PWMs for drive
				//1,3 - Left Motors
				//2,4 - Right Motors
		leftstick(1),		// Left Joystick.
		rightstick(2)		// Right Joystick.
	{
		myRobot.SetInvertedMotor(RobotDrive::kFrontLeftMotor, false);
		myRobot.SetInvertedMotor(RobotDrive::kFrontRightMotor, false);
		myRobot.SetInvertedMotor(RobotDrive::kRearLeftMotor, false);
		myRobot.SetInvertedMotor(RobotDrive::kRearRightMotor, false);
		GetWatchdog().SetExpiration(0.1);
	}

	/**
	 * Drive left & right motors for 2 seconds then stop
	 */
	void Autonomous(void)
	{
		GetWatchdog().SetEnabled(false);
		myRobot.Drive(0.5, 0.0); 	// drive forwards half speed
		Wait(2.0); 				//    for 2 seconds
		myRobot.Drive(0.0, 0.0); 	// stop robot
	}

	/**
	 * Runs the motors with tank steering. 
	 */
	void OperatorControl(void)
	{
		
		// Create and set up a camera instance. first wait for the camera to start
		// if the robot was just powered on. This gives the camera time to boot.
		//Wait(10.0);
		//printf("Getting camera instance
");
		//AxisCamera &camera = AxisCamera::getInstance();
		//printf("Setting camera parameters
");
		//camera.writeResolution(k320x240);
		//camera.writeBrightness(0);

		// set watchdog
		GetWatchdog().SetExpiration(1.0);
		GetWatchdog().SetEnabled(true);
		
		while (IsOperatorControl())
		{
			GetWatchdog().Feed();
			myRobot.TankDrive(leftstick, rightstick); // drive

			// send the dashboard data associated with the I/O ports
			DataPlease.sendIOPortData();
			
			Wait(0.005);				// wait for a motor update time
		}
	}
};

START_ROBOT_CLASS(RobotDemo);

And to top things off here is my DashboardDataFormat.cpp code where I uncommented the things that should have polled the cRio I thought.

#include "DashboardDataFormat.h"

void sendVisionData() {
	Dashboard &dash = DriverStation::GetInstance()->GetHighPriorityDashboardPacker();
	dash.AddCluster(); // wire (2 elements)
	{
		dash.AddCluster(); // tracking data
		{
			dash.AddDouble(1.0); // Joystick X
			dash.AddDouble(135.0); // angle
			dash.AddDouble(3.0); // angular rate
			dash.AddDouble(5.0); // other X
		}
		dash.FinalizeCluster();
		dash.AddCluster(); // target Info (2 elements)
		{
			dash.AddCluster(); // targets
			{
				dash.AddDouble(100.0); // target score
				dash.AddCluster(); // Circle Description (5 elements)
				{
					dash.AddCluster(); // Position (2 elements)
					{
						dash.AddDouble(30.0); // X
						dash.AddDouble(50.0); // Y
					}
					dash.FinalizeCluster();
				}
				dash.FinalizeCluster(); // Position
				dash.AddDouble(45.0); // Angle
				dash.AddDouble(21.0); // Major Radius
				dash.AddDouble(15.0); // Minor Radius
				dash.AddDouble(324.0); // Raw score
			}
			dash.FinalizeCluster(); // targets
		}
		dash.FinalizeCluster(); // target Info
	}
	dash.FinalizeCluster(); // wire
	dash.Finalize();
}

void sendIOPortData() {
	Dashboard &dash = DriverStation::GetInstance()->GetLowPriorityDashboardPacker();
	dash.AddCluster();
	{
		dash.AddCluster();
		{ //analog modules 
			dash.AddCluster();
			{
				for (int i = 1; i <= 8; i++) {
					dash.AddFloat((float) AnalogModule::GetInstance(1)->GetAverageVoltage(i));
					//dash.AddFloat((float) i * 5.0 / 8.0);
				}
			}
			dash.FinalizeCluster();
			dash.AddCluster();
			{
				for (int i = 1; i <= 8; i++) {
					dash.AddFloat((float) AnalogModule::GetInstance(2)->GetAverageVoltage(i));
				}
			}
			dash.FinalizeCluster();
		}
		dash.FinalizeCluster();

		dash.AddCluster();
		{ //digital modules
			dash.AddCluster();
			{
				dash.AddCluster();
				{
					int module = 4;
					dash.AddU8(DigitalModule::GetInstance(module)->GetRelayForward());
					dash.AddU8(DigitalModule::GetInstance(module)->GetRelayReverse());
					dash.AddU16((short)DigitalModule::GetInstance(module)->GetDIO());
					//dash.AddU16((short) 0xAAAA);
					dash.AddU16((short)DigitalModule::GetInstance(module)->GetDIODirection());
					//dash.AddU16((short) 0x7777);
					dash.AddCluster();
					{
						for (int i = 1; i <= 10; i++) {
							dash.AddU8((unsigned char) DigitalModule::GetInstance(module)->GetPWM(i));
							//dash.AddU8((unsigned char) (i-1) * 255 / 9);
						}
					}
					dash.FinalizeCluster();
				}
				dash.FinalizeCluster();
			}
			dash.FinalizeCluster();

			dash.AddCluster();
			{
				dash.AddCluster();
				{
					int module = 6;
					dash.AddU8(DigitalModule::GetInstance(module)->GetRelayForward());
					dash.AddU8(DigitalModule::GetInstance(module)->GetRelayForward());
					dash.AddU16((short)DigitalModule::GetInstance(module)->GetDIO());
					dash.AddU16(DigitalModule::GetInstance(module)->GetDIODirection());
					dash.AddCluster();
					{
						for (int i = 1; i <= 10; i++) {
							dash.AddU8((unsigned char) DigitalModule::GetInstance(module)->GetPWM(i));
							//dash.AddU8((unsigned char) i * 255 / 10);
						}
					}
					dash.FinalizeCluster();
				}
				dash.FinalizeCluster();
			}
			dash.FinalizeCluster();
		}
		dash.FinalizeCluster();

		// Can't read solenoids without an instance of the object
		dash.AddU8((char) 0);
	}
	dash.FinalizeCluster();
	dash.Finalize();
}

I’m not too proud to beg, can someone give me a hint here please. It shouldn’t be this hard. Another two hours gone so far with different variations now. And I still don’t know what the dashboard is expecting and in what order, how do you guys know? Is there documentation of that somewhere, and I would prefer a direct page reference please instead of just telling me to read the manual, because I don’t know what manual to even look in.

Well I got it to work finally but in a weird way. I basically took the Dashboard Code with the torpedo comments removed and moved it over to the IterativeRobot example which we’re gonna use for our robot long term and it started to work.

The only difference is that I am calling it in Iterative only every 0.50 seconds, versus the way I had it, which was calling it every loop. The only thing I can figure is that it wasn’t getting enough time to finish its packing and update the dashboard. So if you are having trouble like I was, remove the commented stuff and change the code to only poll the data at specific period. I don’t know what the minimum is, but 1/2 second is good enough for me. Thanks to those that helped and hopefully this experience will help someone else.

But it still feeds it

Of course, but at a point when its already too late and it will have tripped, thus showing a “Watchdog not fed” error message on the DS.
I point this out so that people will not be alarmed when they see it.

Check out the WPI Robotic’s Library User’s Guide on page 51 - “Sending data to the dashboard”. (Best bet is to follow the hyperlink from the PDF’s table of contents, because someone forgot to put page numbers in the thing :rolleyes:). There is a two-column table on the next page that describes two different LabView data “clusters” - these are essentially the documentation for the data packages that the C++ example code is building up in preparation for sending it to the DS Dashboard that was written (of course) in LabView.

HTH.

Packets go back to the dashboard every 20ms, so it’s pointless to update faster than that.

This structure is only an example, though. It’s true that it does match the default data structures in the Dashboard that is generated when you create a new Dashboard project in LabVIEW, but there is nothing blessed about that format. Delete anything you don’t want and make space for data that will actually help you to debug or to drive.

It’s meant to get you to a good starting point with some examples… not to mandate that you use that data format, cause quite frankly, the data is too low level to be terribly useful (certainly in a driving situation) and reading all that data from the FPGA wastes lots of processor time. The fact that this was intended to be replaced by you with relevant data is even more evident since the real data isn’t populated!

Sorry it was confusing and that you guys had different expectations. This part is supposed to be highly customized.

I have a feeling people would like it even less if it was all just a blank slate giving you no idea how to handle interesting data types, but at least their expectations would be fitting.

Thanks Joe - While we’re on the subject of custom dashboards, may I ask a question?

Is there a good tutorial this year on how to write a custom Dashboard using LabView? I usually stick to text-based programming, and I will be the first to admit that I’m not that proficient in LabView, but I’ve used it enough to know my way around.

Last year, I tried to build a custom dashboard using LabView, and I could not for the life of me figure out how to create the custom data cluster that I needed on the LabView side. The C++ packager side was no problem, but on the LabView side, I kept getting errors when trying to create a new cluster type (sorry, I can’t remember the exact specifics, but I think it was some kind of permission error that wouldn’t let me update an existing library or something?). In any case, the tutorials I was able to find just sort of glanced over that step, as if it was extremely simple and done every day, but try as I might, I had no success. A nice step-by-step tutorial that includes all the details would be very helpful to us LabView novices that really only want to use LabView to create a dashboard and then go back to what we know best - text only! ;).

Thanks!

I recommend the video that Ben put together last year: http://www.lvmastery.com/TipJar2009-02-10

Thanks everyone for their help. I appreciate that the dashboard was made to be customized, but I think that there’s a fundamental assumption in the way examples have been approached for the new control system.

Quite frankly, in our team I do not have any students that are really interested in programming. The teams I have been on have been small, and in our area most of the kids are much more interested in the actual hands on and mechanism stuff of robot building. I am able to convince one or two a year to help me “kluge” together enough code to get the robot to do the basics of what we want. The interest just isn’t there on this aspect.

In the previous years on the previous control system, there was a simple example of how to handle every input, digital and analog, and if you didn’t want to code you could assume some basic functionality out of the default code and you could wire your robot accordingly and it would work. I had assumed that the same functionality would be in the examples provided for the new control system. That was one reason I was getting snippy about having to go into the other *.cpp files and fix them. I had assumed they had been tested with the default system and everything would function.

For those teams with awesome programmers, the default code doesn’t last an evening before they customize it and do really cool stuff with it. But for those of us without the experience and more importantly the student interest on this one facet, the fact is having a sample to start from that functions right away is of immeasurable value. We’re doing as I tell the students, “the monkey see, monkey do” method of programming which doesn’t work unless we have good examples.

I apologize for the high expectations, I’m just showing a different viewpoint from a younger team. Your help has been awesome and very gracious. Thanks again. I imagine you’ll see me some more as we try to fight our way through encoders a little later.

I hope you don’t find that all of the examples are “non-functional” or at least non-expectation-meeting. I believe that the Dashboard is a special case that requires heavy customization to be truly useful. Encoders and many other aspects of the system a pretty straight forward and as such should function out of the box. If you have any trouble with other examples, please continue to let us know so that we can continue to improve the experience for everyone.

-Joe