Our code team is writing a fundraising robot via RobotBase and the HAL, as a bit of an educational foray into the vast jungles of advanced WPILib programming, and have after having a bit of success found that the joysticks do not work. Using HAL_PrintConsoleLine, we determined that the joystick values were all returning 0. Our driver station (Conductor DS) recognizes the joysticks and the joysticks are on the proper id’s. What could be going wrong?
ModularRobot.hpp (custom robot base class):
// Highly modular FRC robot class with threading support
// Works like a standard robot at face value, but has lots of extra cool functionality
// Such as the ability to add modules which can run in threads if you want them to
// As well as extra functionality for internet servers
#include <frc/RobotBase.h>
#include <vector>
#include <thread>
#include <atomic>
#include <string>
#include "c_str_man.hpp"
class ModularRobot;
class Module{ // Not much of a base class, but it serves a purpose!
public:
void init(ModularRobot* robot){
}
void run(unsigned long long tick){
}
};
class ModularRobot : public frc::RobotBase{
private:
std::atomic<bool> m_exit{false};
std::vector<Module> modules;
const char* RobotName;
const char* TeamName;
int TeamNumber;
int mode = -1; // 0 = disabled, 1 = autonomous, 2 = test, 3 = teleop
unsigned long long tick; // Number of iterations since robot began
unsigned long long localTick; // Number of iterations since current operation-mode began
public:
void setData(const char* robotname, const char* teamname, int teamnumber){
RobotName = robotname;
TeamNumber = teamnumber;
TeamName = teamname;
}
virtual void Init() const{
}
virtual void BeginDisabled() const{
}
virtual void DisabledLoop() const{
}
virtual void CleanUpDisabled() const{
}
virtual void BeginTeleop() const{
}
virtual void TeleopLoop(){
}
virtual void CleanUpTeleop() const{
}
virtual void BeginTest() const{
}
virtual void TestLoop() const{
}
virtual void CleanUpTest() const{
}
virtual void BeginAutonomous() const{
}
virtual void AutonomousLoop() const{
}
virtual void CleanUpAutonomous() const{
}
virtual void Loop() const{
} // User mainloop
virtual void ItsOver() const{
}
void addModule(Module module){
modules.push_back(module);
module.init(this); // Module init function should take a pointer to a ModularRobot
}
void loop(){
Loop();
for (Module i : modules){
i.run(tick);
}
}
void StartCompetition(){ // "Final" keeps it from being overriden so stupid people don't damage the program
Init();
HAL_SendConsoleLine(constchar_concat_many(6, RobotName, " by ", TeamName, " (FRC ", "6341", ") is now turning on!"));
HAL_InitializeDriverStation();
HAL_ObserveUserProgramStarting();
while (!m_exit){ // Restructured from the old uglies. This one gives easy-peasy mainlooping without our ugly-mugly turdy-purdy stinky-winky infinite while loop
loop(); // General mainloop
if (IsDisabled()){ // Disabled tasks
HAL_ObserveUserProgramDisabled();
if (mode != 0){
HAL_SendConsoleLine("Begin Disable mode");
localTick = 0; // Reset the local tick counter
BeginDisabled();
}
if (mode == 1){
CleanUpAutonomous();
}
else if (mode == 2){
CleanUpTest();
}
else if (mode == 3){
CleanUpTeleop();
}
DisabledLoop();
mode = 0;
}
else if (IsAutonomous()){ // Autonomous tasks
HAL_ObserveUserProgramAutonomous();
if (mode != 1){
HAL_SendConsoleLine("Begin Autonomous mode");
localTick = 0; // Reset the local tick counter
BeginAutonomous();
}
if (mode == 0){
CleanUpDisabled();
}
else if (mode == 2){
CleanUpTest();
}
else if (mode == 3){
CleanUpTeleop();
}
AutonomousLoop();
mode = 1;
}
else if (IsTest()){ // Test tasks
HAL_ObserveUserProgramTest();
if (mode != 2){
HAL_SendConsoleLine("Never gonna give you up, never gonna let you down, never gonna run around and hurt you");
localTick = 0; // Reset the local tick counter
BeginTest();
}
if (mode == 0){
CleanUpDisabled();
}
else if (mode == 1){
CleanUpAutonomous();
}
else if (mode == 3){
CleanUpTeleop();
}
TestLoop();
mode = 2;
}
else{ // Teleop tasks
HAL_ObserveUserProgramTeleop();
if (mode != 3){
HAL_SendConsoleLine("Begin Teleop mode");
localTick = 0; // Reset the local tick counter
BeginAutonomous();
}
if (mode == 0){
CleanUpDisabled();
}
else if (mode == 1){
CleanUpAutonomous();
}
else if (mode == 2){
CleanUpTest();
}
TeleopLoop();
mode = 3;
}
tick++; // Update the tick counters
localTick++;
}
ItsOver();
}
void EndCompetition() {
m_exit = true;
}
};
Robot.cpp:
// Copyright (c) FIRST and other WPILib contributors.
// Open Source Software; you can modify and/or share it under the terms of
// the WPILib BSD license file in the root directory of this project.
// This used to include Robot.h, now it doesn't because I prefer to write my own class.
// By the way, this is a modular robot program that can be extended quite easily.
#include <frc/DriverStation.h>
#include <frc/livewindow/LiveWindow.h>
#include <frc/shuffleboard/Shuffleboard.h>
#include <hal/DriverStation.h>
#include <networktables/NetworkTable.h>
#include <ctre/Phoenix.h>
#include "rev/CANSparkMax.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h> // FRC ROBOTS SUPPORT THE LINUX KERNEL COMMANDS
#include <frc/Joystick.h>
#include <stdio.h>
#include <unistd.h>
#include "ModularRobot.hpp"
#include "c_str_man.hpp"
enum ServerCommand{
};
enum ServerResponse{
};
class SingleClientCommandServer : public Module{ // Skeleton class that does not handle data. This one only accepts a single client at a time!
struct sockaddr_in serveraddress; // Address of the server
int serverfd; // File descriptor of the server
struct sockaddr clientaddress; // Guess!
int clientfd;
ServerResponse (*GotCommandCallback)(ServerCommand);
public:
SingleClientCommandServer(int port, ServerResponse (*gotCommandCallback)(ServerCommand)){ // All servers bind to every address via INADDR_ANY, so no need for an address field. The second thing is a function pointer - Google It!
GotCommandCallback = gotCommandCallback;
memset(&serveraddress, 0, sizeof(serveraddress)); // Memset is a system-call that writes a value repetitively to memory. Because memory is often full of random bits (for some reason), use this to keep life the universe and everything from imploding.
memset(&clientaddress, 0, sizeof(clientaddress)); // Not necessary but I'm a perfectionist
serveraddress.sin_family = AF_INET; // IPv4
serveraddress.sin_addr.s_addr = INADDR_ANY; // Every bindable address
serveraddress.sin_port = htons(port);
serverfd = socket(AF_INET, SOCK_STREAM, 0); // New socket following the AF_INET internet protocol, the SOCK_STREAM (TCP) transfer protocol, and with 0 flags
bind(serverfd, (struct sockaddr*) &serveraddress, sizeof(serveraddress)); // Bind the server socket to the server address
listen(serverfd, 25); // The socket will keep 25 incoming connections in queue until it tosses them out. This is a bad coding practice for a single-client-at-a-time server, but I want to be safe.
}
void run(){ // Receive data
}
};
class Robot : public ModularRobot{
private:
std::atomic<bool> m_exit{false}; // Multithreaded variable. This is why the code doesn't die!
rev::CANSparkMax DriveLeft{11,rev::CANSparkMax::MotorType::kBrushless};
rev::CANSparkMax DriveLeftSlave{12,rev::CANSparkMax::MotorType::kBrushless};
rev::CANSparkMax DriveRight{13,rev::CANSparkMax::MotorType::kBrushless};
rev::CANSparkMax DriveRightSlave{14,rev::CANSparkMax::MotorType::kBrushless};
TalonSRX ArmMaster{15};
TalonSRX ArmSlave{16};
frc::Joystick Controls{5};
int mode = 0; // 0 = disabled, 1 = autonomous, 2 = test, 3 = teleop
// Below this line is server stuff. There may be multiple servers and in the case that there are we must make sure that they follow this port specification:
// Command servers are from 8000 - 8099
// Data broadcast servers are from 8100 - 8199 (For things like processor statistics and motor information etc)
// Media streaming (video/audio) is from 8200 - 8299
// General purpose servers (do any combination of the above) are from 8300 - 8399
public:
Robot(){
//HAL_SendConsoleLine("This is a console line");
setData("Robot", "FRC Team", TEAM_NUMBER);
}
void Init(){
DriveLeftSlave.Follow(DriveLeft);
DriveRightSlave.Follow(DriveRight);
}
void TeleopLoop(){
HAL_SendConsoleLine(inttostring(Controls.GetX()));
usleep(2000000);
}
void BeginTest(){
DriveLeft.Set(0.2);
DriveRight.Set(0.2);
}
void CleanUpTest(){
DriveLeft.Set(0);
DriveRight.Set(0);
}
};
#ifndef RUNNING_FRC_TESTS
int main() {
return frc::StartRobot<Robot>();
}
#endif
The calls to stringtoint and constchar_concat_many are from a dependency (which provides string manipulation) and are unimportant.
Thanks in advance, and please no hate for our unconventional methods!