BMS NetworkTables/Shuffleboard

Hello all,

I am currently writing code for a Battery Management System which is being read from on an Arduino and then sent to a RoboRIO for data logging to NetworkTables and Shuffleboard. Can anyone possibly tell me what is wrong with the following code that this may not be working?

package frc.robot.subsystems;

import edu.wpi.first.wpilibj.SerialPort;
import edu.wpi.first.wpilibj2.command.SubsystemBase;
import java.util.HashMap;
import edu.wpi.first.wpilibj.shuffleboard.Shuffleboard;
import edu.wpi.first.networktables.GenericEntry;
import edu.wpi.first.networktables.NetworkTable;
import edu.wpi.first.networktables.NetworkTableInstance;

public class BMS extends SubsystemBase{
    SerialPort arduinoBMS;

    
    private final NetworkTableInstance ntInstance = NetworkTableInstance.getDefault();
    private final NetworkTable networkTable;
    private HashMap<String, GenericEntry> shuffleboardEntries = new HashMap<String, GenericEntry>();
    
    public BMS() {
        arduinoBMS = new SerialPort(115200, SerialPort.Port.kMXP);
        networkTable = ntInstance.getTable("BMS");
        
        createDashBoardData();
    }

    @Override
    public void periodic() {
        parseArduinoOutput();
    }

    private void createDashBoardData() {
        for (int i = 1; i <= 12; i++) {
            String cellKey = "cell-" + i;
            String cellLabel = "Cell " + i + " Voltage";
            shuffleboardEntries.put(cellKey, Shuffleboard.getTab("BMS").add(cellLabel, 0).withSize(1, 1).withPosition((i - 1) % 4, (i - 1) / 4).getEntry());
        }
    }
    
    public void parseArduinoOutput() {
        String arduinoOutput = arduinoBMS.readString();
        String[] cellVoltages = arduinoOutput.trim().split("\\s+");

        for (int i = 0; i < cellVoltages.length; i++) {
            String cellName = "cell-" + (i + 1);
            double voltage = Double.parseDouble(cellVoltages[i]);
            System.out.println(cellName + ": " + voltage);

            // Update NetworkTable and Shuffleboard
            networkTable.getEntry(cellName).setDouble(voltage);
            shuffleboardEntries.get(cellName).setDouble(voltage);
        }

    }
}

Thank you!

Sure, you’re not calling that code anywhere, so it can’t work.

Okay okay, that’s obviously sarcasm. In all seriousness, without at least a description of the problem you’re having, it’s really hard to even guess at what the issue is.

Also, it’s generally preferred if you provide the entire project as a git repo (hosted on github, gitlab, etc). Often the issue isn’t within the specific class or segment of code provided, and without it we would never be able to tell for sure.

1 Like

Thank you for the posting advice, noted for the future!

Below is the repo for the branch “actuators” which this code was written in. I believe you are definitely correct with your sarcasm, in that I am likely calling it all wrong. This is the first time I am writing something that is not updating the networktables and shuffleboard in conjunction with hardware updates, so I have been struggling with where and how to call it. In this branch, all that exists is the subsystem without any references to it, because again, I am not very sure how to reference it correctly. Hopefully this makes it easier to help me. Thank you!

Actuators Branch

You need to instantiate the BMS subsystem in your robot container. It’ll automatically be registered with the command scheduler to call the periodic() function.

Also, working with the network table entries directly isn’t necessary if all you’re doing is pushing data. You can use ShuffleboardTab#addDouble and pass in a supplier, which can read from a cached field instead of from the serial port. (This has the added bonus of letting you read that same cached data somewhere else if you need it, without having to do another serial read).

private double[] m_voltages = new double[12];

@Override
public void periodic() {
  m_voltages = Arrays.stream(arduinoBMS.readString().trim().split("\\s+")).mapToDouble(Double::parseDouble).toArray();
}

private void createDashBoardData() {
  for (int i = 1; i <= 12; i++) {
    String cellLabel = "Cell " + i + " Voltage";
    Shuffleboard.getTab("BMS")
      .addDouble(cellLabel, () -> m_voltages[i])
      .withSize(1, 1)
      .withPosition((i - 1) % 4, (i - 1) / 4);
  }
}

I’m assuming that these serial port reads are fast and won’t block for longer than a few milliseconds; if they take longer, consider using a Notifier thread to periodically read data from the serial port without blocking the main thread.

In the robot container is instantiating as follows all that need to happen, or does it need to be referenced?

private final BMS s_BMS = new BMS();

Also thank you for the note on notifiers, will definitely look into that if this interrupt becomes problematic.

Instantiating it is enough. The constructor will automatically add it to the command scheduler. Just make sure the scheduler runs in the robot’s periodic method

Awesome, thank you so much gonna give it a try tonight!