Frc:: Joystick::GetX always returns 0

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!

Iirc, the frc::Joystick class will not work if only the hal initialize calls are called and not the frc::DriverStation calls that the normal base classes call. Only other idea is to ensure the joystick is in index 5 in the ds, and maybe try with 0. The way joysticks are skipped in the protocol is weird, and I’m not sure if that ever is tested in conductor.

This made me think a thought. I want to be careful not to distract too much from the original question. At the same time, I’d like to make a request:

This is one of a handful in a series of posts exploring learning by starting from an architectural layer different than most FRC teams start from.

I’m interested to understand what was learned through this process - particularly if it yielded unique lessons compared to a more “traditional” approach.

I’d like to understand where student(s) started from, where they got to, and what benefits they saw along the way. Less from a robot-performance perspective, more from a personal-growth perspective.

I am nearly certain the outcome won’t be “nothing useful”. It’s still a useful sample point. If there’s any broader lessons to be learned, I am really really really interested. Even If the results are unique and apparently applicable only to an audience of one, that’s ok. Still a useful sample point.

My general task as mentor is to meet students where they are at, and bring them up to enough proficiency to execute excellent FRC robots. Any sample points to help with this endeavor are, I believe, useful to the community here.

OP - I want to be careful to respect your time. But if you have the ability to do a write-up on your approach to software development and training, especially with a focus on what the pedagogical outcomes are, I would appreciate understanding more about you (and your team’s) motivations, background, execution, and results.

Even if our goals and priorities don’t align, I still believe I have a great deal I can learn from you, and do indeed desire to learn it.

3 Likes

FYI, you seem to have included your team name and number in your code. Not sure if that was intentional given your anonymous profile.

Going back to the original question, I have no idea why [non-standard thing] doesn’t work. Not many people will. These questions are probably better aimed directly towards the WPILib team (GitHub issues, DMs maybe?) than towards the whole of CD. Either way, you’re still going off the paved road. You have no guarantee of support. If you have to ask for directions every step of the way, I would urge you to reconsider your approach so that you can actually finish your fundraising robot in a timely manner.

3 Likes

You probably want to BeginTeleop() there. Looks like a copy+paste bug.

But since that function is empty, that is unlikely to fix your joystick problem. Have you inspected the code for the Joystick class you are using to see if it requires additional initialization? Perhaps it has an Enable() function of some kind.

One other thought: WPILIB maps the available system joysticks/keyboard/controllers into a table that is used to reference them. How are you initializing/achieving the mapping of system joysticks into the logical space? Are you certain that there is a physical joystick mapped to the index you are reading from?

if you have the ability to do a write-up on your approach to software development and training, especially with a focus on what the pedagogical outcomes are, I would appreciate understanding more about you (and your team’s) motivations, background, execution, and results.

I’ll have to talk to the head coder, he’ll probably be able to make a write-up for you

1 Like

trainingandsoftwaredevelopment.txt (1.8 KB)
Sorry it took so long to get back to you - I hope you find it useful!

1 Like

For whoever doesn’t feel like reading a text file…

4 Likes

Sounds like something some GitHub issues could be filed about so we can improve the implementation, documentation, or both: Issues · wpilibsuite/allwpilib · GitHub

Its not a bad thing - just an interesting way of writing the api