My team is working on an off-season project that uses IR-thermometers to track heat. I am using this arduino library to guide my programming:
I have been unable to get any readings from either of our two sensors. When I try to address the sensors it aborts. I used the default address, so I don’t know why this is happening. Any help would be greatly appreciated.
Thanks,
Ethan
If it helps, here is my code so far:
package org.usfirst.frc.team1572.robot;
import org.usfirst.frc.team1572.robot.commands.SetSpeeds;
import org.usfirst.frc.team1572.robot.subsystems.SwerveDrive;
import edu.wpi.first.wpilibj.I2C;
import edu.wpi.first.wpilibj.I2C.Port;
import edu.wpi.first.wpilibj.IterativeRobot;
import edu.wpi.first.wpilibj.command.Command;
import edu.wpi.first.wpilibj.command.Scheduler;
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
/**
-
The VM is configured to automatically run this class, and to call the
-
functions corresponding to each mode, as described in the IterativeRobot
-
documentation. If you change the name of this class or the package after
-
creating this project, you must also update the manifest file in the resource
-
directory.
*/
public class Robot extends IterativeRobot {public static OI oi;
public static RobotMap robotMap;
public static SwerveDrive swerveDrive;
I2C sensor;
byte] buffer;
I2C] sensors;
I2C] sensors2;Command autonomousCommand;
SendableChooser<Command> chooser = new SendableChooser<>();/**
- This function is run when the robot is first started up and should be
- used for any initialization code.
/
@Override
public void robotInit() {
oi = new OI();
oi.init();
robotMap = new RobotMap();
robotMap.init();
swerveDrive = new SwerveDrive();
sensor = new I2C(Port.kOnboard, 0x5A);
sensors = new I2C[256]; //try to address all addresses for sanity check
int address = 0x00;
for(int i=0; i<256; i++) {
sensors = new I2C(Port.kOnboard, address);
address += 1;
}
sensors2 = new I2C[256]; //try to address all addresses via mxp for sanity check
int address2 = 0x00;
for(int i=0; i<256; i++) {
sensors2* = new I2C(Port.kMXP, address2);
address2 += 1;
}
buffer = new byte[3];
// chooser.addObject(“My Auto”, new MyAutoCommand());
SmartDashboard.putData(“Auto mode”, chooser);
}
/**
- This function is called once each time the robot enters Disabled mode.
- You can use it to reset any subsystem information you want to clear when
- the robot is disabled.
*/
@Override
public void disabledInit() {
}
@Override
public void disabledPeriodic() {
Scheduler.getInstance().run();
}/**
-
This autonomous (along with the chooser code above) shows how to select
-
between different autonomous modes using the dashboard. The sendable
-
chooser code works with the Java SmartDashboard. If you prefer the
-
LabVIEW Dashboard, remove all of the chooser code and uncomment the
-
getString code to get the auto name from the text box below the Gyro
-
You can add additional auto modes by adding additional commands to the
-
chooser code above (like the commented example) or additional comparisons
-
to the switch structure below with additional strings & commands.
*/
@Override
public void autonomousInit() {
autonomousCommand = chooser.getSelected();/*
- String autoSelected = SmartDashboard.getString(“Auto Selector”,
- “Default”); switch(autoSelected) { case “My Auto”: autonomousCommand
- = new MyAutoCommand(); break; case “Default Auto”: default:
- autonomousCommand = new ExampleCommand(); break; }
*/
// schedule the autonomous command (example)
if (autonomousCommand != null)
autonomousCommand.start();
}
/**
- This function is called periodically during autonomous
*/
@Override
public void autonomousPeriodic() {
Scheduler.getInstance().run();
}
@Override
public void teleopInit() {
// This makes sure that the autonomous stops running when
// teleop starts running. If you want the autonomous to
// continue until interrupted by another command, remove
// this line or comment it out.
if (autonomousCommand != null){
autonomousCommand.cancel();
}
Scheduler.getInstance().add(new SetSpeeds());
}/**
- This function is called periodically during operator control
/
@Override
public void teleopPeriodic() {
Scheduler.getInstance().run();
System.out.println(sensor.addressOnly());
int address = 0; //try to address all addresses for sanity check
for(int i = 0; i < 256; i++) {
if(!sensors.addressOnly()) {
address = 0x00 + i;
}
}
int address2 = 0; //try to address all addresses via mxp for sanity check
for(int i = 0; i < 256; i++) {
if(!sensors2*.addressOnly()) {
address2 = 0x00 + i;
}
}
System.out.println(address);
System.out.println(address2);
System.out.println(sensor.read(0x07, 3, buffer));
System.out.println("lsb " + buffer[0]);
System.out.println("msb " + buffer[1]);
System.out.println("crc " + buffer[2]);
double variable = (buffer[1] << 8) | buffer[0];
System.out.println("hopefully this is right " + variable);
variable *= 0.02;
System.out.println("hopefully this is more right " + variable);
}
/**
- This function is called periodically during test mode
/
@Override
public void testPeriodic() {
LiveWindow.run();
}
}***
Did you wire both sensors on the same I2C bus?
Do you have pull-up resistors on the SDA / SCL pins?
I did not have both sensors on the bus at the same time because I need to establish a connection before I can change the address of one. I do not have pull-up resistors on the SCL or SDA pins. Should I have resistors? If so, can you point me to some cheap ones?
If you’re using the RoboRIO, (finally reading the datasheet) it looks like they pull-up resistors are included.
Can you step me through the code and describe what you’re trying to do?
I did some more research and the pullup resistors are included in the RoboRIO for the SDA and SCL pins.
I used the Sparkfun MLX90614 Arduino Library (https://github.com/sparkfun/SparkFun_MLX90614_Arduino_Library/blob/master/src/) to determine how to interface with the sensors. My main problem however is that when I use the addressOnly function to make sure I have connection, it returns true for abort. All other transactions I have tried have also aborted, and the bytes that I use to store data remains 0.
Here is my code with comments to guide you through (just for working with one sensor) -
package org.usfirst.frc.team1572.robot;
import org.usfirst.frc.team1572.robot.commands.SetSpeeds;
import org.usfirst.frc.team1572.robot.subsystems.SwerveDrive;
import edu.wpi.first.wpilibj.I2C;
import edu.wpi.first.wpilibj.I2C.Port;
import edu.wpi.first.wpilibj.IterativeRobot;
import edu.wpi.first.wpilibj.Timer;
import edu.wpi.first.wpilibj.command.Command;
import edu.wpi.first.wpilibj.command.Scheduler;
import edu.wpi.first.wpilibj.livewindow.LiveWindow;
import edu.wpi.first.wpilibj.smartdashboard.SendableChooser;
import edu.wpi.first.wpilibj.smartdashboard.SmartDashboard;
public class Robot extends IterativeRobot {
public static OI oi;
public static RobotMap robotMap;
public static SwerveDrive swerveDrive;
I2C sensor;
byte] buffer; //array to store data from sensor
//I2C] sensors;
//I2C] sensors2;
Command autonomousCommand;
SendableChooser<Command> chooser = new SendableChooser<>();
/**
* This function is run when the robot is first started up and should be
* used for any initialization code.
*/
@Override
public void robotInit() {
oi = new OI();
oi.init();
robotMap = new RobotMap();
robotMap.init();
swerveDrive = new SwerveDrive();
sensor = new I2C(Port.kOnboard, 0x5A); //0x5A is default address
/*sensors = new I2C[256]; //try to address all possible addresses for sanity check
int address = 0x00;
for(int i=0; i<256; i++) {
sensors* = new I2C(Port.kOnboard, address);
address += 1;
}
sensors2 = new I2C[256]; //try to address all possible addresses via mxp for sanity check
int address2 = 0x00;
for(int i=0; i<256; i++) {
sensors2* = new I2C(Port.kMXP, address2);
address2 += 1;
}*/
buffer = new byte[3];
// chooser.addObject("My Auto", new MyAutoCommand());
SmartDashboard.putData("Auto mode", chooser);
}
/**
* This function is called once each time the robot enters Disabled mode.
* You can use it to reset any subsystem information you want to clear when
* the robot is disabled.
*/
@Override
public void disabledInit() {
}
@Override
public void disabledPeriodic() {
Scheduler.getInstance().run();
}
/**
* This autonomous (along with the chooser code above) shows how to select
* between different autonomous modes using the dashboard. The sendable
* chooser code works with the Java SmartDashboard. If you prefer the
* LabVIEW Dashboard, remove all of the chooser code and uncomment the
* getString code to get the auto name from the text box below the Gyro
*
* You can add additional auto modes by adding additional commands to the
* chooser code above (like the commented example) or additional comparisons
* to the switch structure below with additional strings & commands.
*/
@Override
public void autonomousInit() {
autonomousCommand = chooser.getSelected();
/*
* String autoSelected = SmartDashboard.getString("Auto Selector",
* "Default"); switch(autoSelected) { case "My Auto": autonomousCommand
* = new MyAutoCommand(); break; case "Default Auto": default:
* autonomousCommand = new ExampleCommand(); break; }
*/
// schedule the autonomous command (example)
if (autonomousCommand != null)
autonomousCommand.start();
}
/**
* This function is called periodically during autonomous
*/
@Override
public void autonomousPeriodic() {
Scheduler.getInstance().run();
}
@Override
public void teleopInit() {
// This makes sure that the autonomous stops running when
// teleop starts running. If you want the autonomous to
// continue until interrupted by another command, remove
// this line or comment it out.
if (autonomousCommand != null){
autonomousCommand.cancel();
}
Scheduler.getInstance().add(new SetSpeeds());
/* code for setting a new address
* clears EEPROM register
int clear = 0;
int crc1;
int lsb1 = clear & 0x00FF;
int msb1 = (clear >> 8);
//cyclic redundancy check copied from library to send with data to avoid errors
crc1 = CRC8.crc8(0, (0x5A << 1));
crc1 = CRC8.crc8(crc1, 0x2E); //0x2E is register for new address
crc1 = CRC8.crc8(crc1, lsb1);
crc1 = CRC8.crc8(crc1, msb1);
sensor.write(0x2E, lsb1);
sensor.write(0x2E, msb1);
sensor.write(0x2E, crc1);
Timer.delay(0.005);
//writes new address to EEPROM register
int newAddress = 0x5B;
int crc2;
int lsb2 = newAddress & 0x00FF;
int msb2 = (newAddress >> 8);
crc2 = CRC8.crc8(0, (0x5A << 1));
crc2 = CRC8.crc8(crc2, 0x2E); //0x2E is register for new address
crc2 = CRC8.crc8(crc2, lsb2);
crc2 = CRC8.crc8(crc2, msb2);
sensor.write(0x2E, lsb2);
sensor.write(0x2E, msb2);
sensor.write(0x2E, crc2);
Timer.delay(0.005);*/
}
/**
* This function is called periodically during operator control
*/
@Override
public void teleopPeriodic() {
Scheduler.getInstance().run();
System.out.println(sensor.addressOnly()); //try to address to ensure connection - always returns aborted
/*int address = 0; //try to address all addresses for sanity check
for(int i = 0; i < 256; i++) { //should return correct address but none are correct
if(!sensors*.addressOnly()) {
address = 0x00 + i;
}
}
int address2 = 0; //try to address all addresses via mxp for sanity check
for(int i = 0; i < 256; i++) { //should return correct address but none are correct
if(!sensors2*.addressOnly()) {
address2 = 0x00 + i;
}
}
System.out.println(address);
System.out.println(address2);*/
System.out.println(sensor.read(0x07, 3, buffer)); //read 3 bytes from object temp register and store in buffer
//always returns aborted
System.out.println("lsb " + buffer[0]); //least significant byte
System.out.println("msb " + buffer[1]); //most significant byte
System.out.println("crc " + buffer[2]); //crc value to check for errors, I decided to skip the check for now to reduce complication
double variable = (buffer[1] << 8) | buffer[0];//converts bytes to temp using formula in library
variable *= 0.02; //converts bytes to temp using formula in library
System.out.println("Temperature in Kelvin " + variable);
}
/**
* This function is called periodically during test mode
*/
@Override
public void testPeriodic() {
LiveWindow.run();
}
}
Thank you for your help!****
I looked more into the resistor situation and I found out something that might be a problem. The sensor requires a 4.7KOhm pullup, but the RoboRIO only has a 2.2KOhm pullup. Maybe this isn’t enough to increase the signal. Should I get resistors to test this?
I2C works by “pulling down” the signal lines, or connecting them to ground. The “pull-up” resistor works by making the signal line normally have the voltage applied (in the case of the roborio 3.3v).
The 4.7KOhm specified is the highest value resistance you would want - any value higher and there might not be enough time to pullup back to signal voltage. Any resistor you would add would be in parallel to the internal 2.2KOhm, and that would decrease the resistance.
TLDR; don’t add another resistor, that isn’t your problem.
EDIT -
https://commons.wikimedia.org/wiki/File:I2C.svg#/media/File:I2C.svg
In the figure, Rp is the pull-up resistor. When the device wants to communicate on the data line, it shorts the bus to ground.
I’ve googled for some WPI roborio I2C code examples, see if this helps you.
https://forums.usfirst.org/forum/general-discussions/first-programs/first-robotics-competition/competition-discussion/programming-aa/java-ad/14687-using-i2c-on-the-roborio-for-sensors