Squashed 'third_party/allwpilib/' changes from 66b57f032..e473a00f9

e473a00f9 [wpiutil] Base64: Add unsigned span/vector variants (#3702)
52f2d580e [wpiutil] raw_uv_ostream: Add reset() (#3701)
d7b1e3576 [wpiutil] WebSocket: move std::function (#3700)
93799fbe9 [examples] Fix description of TrapezoidProfileSubsystem (#3699)
b84644740 [wpimath] Document pose estimator states, inputs, and outputs (#3698)
2dc35c139 [wpimath] Fix classpaths for JNI class loads (#3697)
2cb171f6f [docs] Set Doxygen extract_all to true and fix Doxygen failures (#3695)
a939cd9c8 [wpimath] Print uncontrollable/unobservable models in LQR and KF (#3694)
d5270d113 [wpimath] Clean up C++ StateSpaceUtil tests (#3692)
b20903960 [wpimath] Remove redundant discretization tests from StateSpaceUtilTest (#3689)
c0cb545b4 [wpilibc] Add deprecated Doxygen attribute to SpeedController (#3691)
35c9f66a7 [wpilib] Rename PneumaticsHub to PneumaticHub (#3686)
796d03d10 [wpiutil] Remove unused LLVM header (#3688)
8723caf78 [wpilibj] Make Java TrapezoidProfile.Constraints an immutable class (#3687)
187f50a34 [wpimath] Catch incorrect parameters to state-space models earlier (#3680)
8d04606c4 Replace instances of frc-characterization with SysId (NFC) (#3681)
b82d4f6e5 [hal, cscore, ntcore] Use WPI common handle type base
87e34967e [wpiutil] Add synchronization primitives
e32499c54 [wpiutil] Add ParallelTcpConnector (#3655)
aa0b49228 [wpilib] Remove redundant "quick turn" docs for curvature drive (NFC) (#3674)
57301a7f9 [hal] REVPH: Start closed-loop compressor control on init (#3673)
d1842ea8f [wpilib] Improve interrupt docs (NFC) (#3679)
558151061 [wpiutil] Add DsClient (#3654)
181723e57 Replace `.to<double>()` and `.template to<double>()` with `.value()` (#3667)
6bc1db44b [commands] Add pointer overload of AddRequirements (artf6003) (#3669)
737b57ed5 [wpimath] Update to drake v0.35.0 (#3665)
4d287d1ae [build] Upgrade WPIJREArtifact to JRE 2022-11.0.12u5 (#3666)
f26eb5ada [hal] Fix another typo (get -> gets) (NFC) (#3663)
94ed275ba [hal] Fix misspelling (numer -> number) (NFC) (#3662)
ac2f44da3 [wpiutil] uv: use move for std::function (#3653)
75fa1fbfb [wpiutil] json::serializer: Optimize construction (#3647)
5e689faea [wpiutil] Import MessagePack implementation (mpack) (#3650)
649a50b40 [wpiutil] Add LEB128 byte-by-byte reader (#3652)
e94397a97 [wpiutil] Move json_serializer.h to public headers (#3646)
4ec58724d [wpiutil] uv::Tcp: Clarify SetNoDelay documentation (#3649)
8cb294aa4 [wpiutil] WebSocket: Make Shutdown() public (#3651)
2b3a9a52b [wpiutil] json: Fix map iterator key() for std::string_view (#3645)
138cbb94b [wpiutil] uv::Async: Add direct call for no-parameter specialization (#3648)
e56d6dea8 [ci] Update testbench pool image to ubuntu-latest (#3643)
43f30e44e [build] Enable comments in doxygen source files (#3644)
9e6db17ef [build] Enable doxygen preprocessor expansion of WPI_DEPRECATED (#3642)
0e631ad2f Add WPILib version to issue template (#3641)
6229d8d2f [build] Docs: set case_sense_names to false (#3392)
4647d09b5 [docs] Fix Doxygen warnings, add CI docs lint job (#3639)
4ad3a5402 [hal] Fix PWM allocation channel (#3637)
05e5feac4 [docs] Fix brownout docs (NFC) (#3638)
67df469c5 [examples] Remove old command-based templates and examples (#3263)
689e9ccfb [hal, wpilib] Add brownout voltage configuration (#3632)
9cd4bc407 [docs] Add useLocal to avoid using installer artifacts (#3634)
61996c2bb [cscore] Fix Java direct callback notifications (#3631)
6d3dd99eb [build] Update to newest native-utils (#3633)
f0b484892 [wpiutil] Fix StringMap iterator equality check (#3629)
8352cbb7a Update development build instructions for 2022 (#3616)
6da08b71d [examples] Fix Intermediate Vision Java Example description (#3628)
5d99059bf [wpiutil] Remove optional.h (#3627)
fa41b106a [glass, wpiutil] Add missing format args (#3626)
4e3fd7d42 [build] Enable Zc:__cplusplus for Windows (#3625)
791d8354d [build] Suppress deprecation/removal warnings for old commands (#3618)
10f19e6fc [hal, wpilib] Add REV PneumaticsHub (#3600)
4c61a1305 [ntcore] Revert to per-element copy for toNative() (#3621)
7b3f62244 [wpiutil] SendableRegistry: Print exception stacktrace (#3620)
d347928e4 [hal] Use better error for when console out is enabled while attempting to use onboard serial port (#3622)
cc31079a1 [hal] Use setcap instead of setuid for setting thread priorities (#3613)
4676648b7 [wpimath] Upgrade to Drake v0.34.0 (#3607)
c7594c911 [build] Allow building wpilibc in cmake without cscore and opencv (#3605)
173cb7359 [wpilib] Add TimesliceRobot (#3502)
af295879f [hal] Set error status for I2C port out of range (#3603)
95dd20a15 [build] Enable spotbugs (#3601)
b65fce86b [wpilib] Remove Timer lock in wpilibj and update docs (#3602)
3b8d3bbcb Remove unused and add missing deprecated.h includes (#3599)
f9e976467 [examples] Rename DriveTrain classes to Drivetrain (#3594)
118a27be2 [wpilib] Add Timer tests (#3595)
59c89428e [wpilib] Deprecate Timer::HasPeriodPassed() (#3598)
202ca5e78 Force C++17 in .clang-format (#3597)
d6f185d8e Rename tests for consistency (#3592)
54ca474db [ci] Enable asan and tsan in CI for tests that pass (#3591)
1ca383b23 Add Debouncer (#3590)
179fde3a7 [build] Update to 2022 native utils and gradle 7 (#3588)
50198ffcf [examples] Add Mechanism2d visualization to Elevator Sim (#3587)
a446c2559 [examples] Synchronize C++ and Java Mechanism2d examples (#3589)
a7fb83103 [ci] clang-tidy: Generate compilation commands DB with Gradle (#3585)
4f5e0c9f8 [examples] Update ArmSimulation example to use Mechanism2d (#3572)
8164b91dc [CI] Print CMake test output on failure (#3583)
4d5fca27e [wpilib] Impove Mechanism2D documentation (NFC) (#3584)
fe59e4b9f Make C++ test names more consistent (#3586)
5c8868549 [wpilibc] Fix C++ MechanisimRoot2D to use same NT entries as Java/Glass (#3582)
9359431ba [wpimath] Clean up Eigen usage
72716f51c [wpimath] Upgrade to Eigen 3.4
382deef75 [wpimath] Explicitly export wpimath symbols
161e21173 [ntcore] Match standard handle layout, only allow 16 instances (#3577)
263a24811 [wpimath] Use jinja for codegen (#3574)
725251d29 [wpilib] Increase tolerances of DifferentialDriveSimTest (#3581)
4dff87301 [wpimath] Make LinearFilter::Factorial() constexpr (#3579)
60ede67ab [hal, wpilib] Switch PCM to be a single object that is allowed to be duplicated (#3475)
906bfc846 [build] Add CMake build support for sanitizers (#3576)
0d4f08ad9 [hal] Simplify string copy of joystick name (#3575)
a52bf87b7 [wpiutil] Add Java function package (#3570)
40c7645d6 [wpiutil] UidVector: Return old object from erase() (#3571)
5b886a23f [wpiutil] jni_util: Add size, operator[] to JArrayRef (#3569)
65797caa7 [sim] Fix halsim_ds_socket stringop overflow warning from GCC 10 (#3568)
66abb3988 [hal] Update runtime enum to allow selecting roborio 2 (#3565)
95a12e0ee [hal] UidSetter: Don't revert euid if its already current value (#3566)
27951442b [wpimath] Use external Eigen headers only (#3564)
c42e053ae [docs] Update to doxygen 1.9.2 (#3562)
e7048c8c8 [docs] Disable doxygen linking for common words that are also classes (#3563)
d8e0b6c97 [wpilibj] Fix java async interrupts (#3559)
5e6c34c61 Update to 2022 roborio image (#3537)
828f073eb [wpiutil] Fix uv::Buffer memory leaks caught by asan (#3555)
2dd5701ac [cscore] Fix mutex use-after-free in cscore test (#3557)
531439198 [ntcore] Fix NetworkTables memory leaks caught by asan (#3556)
3d9a4d585 [wpilibc] Fix AnalogTriggerOutput memory leak reported by asan (#3554)
54eda5928 [wpiutil] Ignore ubsan vptr upcast warning in SendableHelper moves (#3553)
5a4f75c9f [wpilib] Replace Speed controller comments with motor controller (NFC) (#3551)
7810f665f [wpiutil] Fix bug in uleb128 (#3540)
697e2dd33 [wpilib] Fix errant jaguar reference in comments (NFC) (#3550)
936c64ff5 [docs] Enable -linksource for javadocs (#3549)
1ea654954 [build] Upgrade CMake build to googletest 1.11.0 (#3548)
32d9949e4 [wpimath] Move controller tests to wpimath (#3541)
01ba56a8a [hal] Replace strncpy with memcpy (#3539)
e109c4251 [build] Rename makeSim flag to forceGazebo to better describe what it does (#3535)
e4c709164 [docs] Use a doxygen theme and add logo (#3533)
960b6e589 [wpimath] Fix Javadoc warning (#3532)
82eef8d5e [hal] Remove over current fault HAL functions from REV PDH (#3526)
aa3848b2c [wpimath] Move RobotDriveBase::ApplyDeadband() to MathUtil (#3529)
3b5d0d141 [wpimath] Add LinearFilter::BackwardFiniteDifference() (#3528)
c8fc715fe [wpimath] Upgrade drake files to v0.33.0 (#3531)
e5fe3a8e1 [build] Treat javadoc warnings as errors in CI and fix warnings (#3530)
e0c6cd3dc [wpimath] Add an operator for composing two Transform2ds (#3527)
2edd510ab [sim] Add sim wrappers for sensors that use SimDevice (#3517)
2b3e2ebc1 [hal] Fix HAL Notifier thread priority setting (#3522)
ab4cb5932 [gitignore] Update gitignore to ignore bazel / clion files (#3524)
57c8615af [build] Generate spotless patch on failure (#3523)
b90317321 Replace std::cout and std::cerr with fmt::print() (#3519)
10cc8b89c [hal] [wpilib] Add initial support for the REV PDH (#3503)
5d9ae3cdb [hal] Set HAL Notifier thread as RT by default (#3482)
192d251ee [wpilibcIntegrationTests] Properly disable DMA integration tests (#3514)
031962608 [wpilib] Add PS4Controller, remove Hand from GenericHID/XboxController (#3345)
25f6f478a [wpilib] Rename DriverStation::IsOperatorControl() to IsTeleop() (#3505)
e80f09f84 [wpilibj] Add unit tests (#3501)
c159f91f0 [wpilib] Only read DS control word once in IterativeRobotBase (#3504)
eb790a74d Add rio development docs documenting myRobot deploy tasks (#3508)
e47451f5a [wpimath] Replace auto with Eigen types (#3511)
252b8c83b Remove Java formatting from build task in CI (#3507)
09666ff29 Shorten Gazebo CI build (#3506)
baf2e501d Update myRobot to use 2021 java (#3509)
5ac60f0a2 [wpilib] Remove IterativeRobotBase mode init prints (#3500)
fb2ee8ec3 [wpilib] Add TimedRobot functions for running code on mode exit (#3499)
94e0db796 [wpilibc] Add more unit tests (#3494)
b25324695 [wpilibj] Add units to parameter names (NFC) (#3497)
1ac73a247 [hal] Rename PowerDistributionPanel to PowerDistribution (#3466)
2014115bc [examples] frisbeebot: Fix typo and reflow comments (NFC) (#3498)
4a944dc39 [examples] Consistently use 0 for controller port (#3496)
3838cc4ec Use unicode characters in docs equations (#3487)
85748f2e6 [examples] Add C++ TankDrive example (#3493)
d7b8aa56d [wpilibj] Rename DriverStation In[Mode] functions to follow style guide (#3488)
16e096cf8 [build] Fix CMake Windows CI (#3490)
50af74c38 [wpimath] Clean up NumericalIntegration and add Discretization tests (#3489)
bfc209b12 Automate fmt update (#3486)
e7f9331e4 [build] Update to Doxygen 1.9.1 (#3008)
ab8e8aa2a [wpimath] Update drake with upstream (#3484)
1ef826d1d [wpimath] Fix IOException path in WPIMath JNI (#3485)
52bddaa97 [wpimath] Disable iostream support for units and enable fmtlib (#3481)
e4dc3908b [wpiutil] Upgrade to fmtlib 8.0.1 (#3483)
1daadb812 [wpimath] Implement Dormand-Prince integration method (#3476)
9c2723391 [cscore] Add [[nodiscard]] to GrabFrame functions (#3479)
7a8796414 [wpilib] Add Notifier integration tests (#3480)
f8f13c536 [wpilibcExamples] Prefix decimal numbers with 0 (#3478)
1adb69c0f [ntcore] Use "NetworkTables" instead of "Network Tables" in NT specs (#3477)
5f5830b96 Upload wpiformat diff if one exists (#3474)
9fb4f35bb [wpimath] Add tests for DARE overload with Q, R, and N matrices (#3472)
c002e6f92 Run wpiformat (#3473)
c154e5262 [wpilib] Make solenoids exclusive use, PCM act like old sendable compressor (#3464)
6ddef1cca [hal] JNI setDIO: use a boolean and not a short (#3469)
9d68d9582 Remove extra newlines after open curly braces (NFC) (#3471)
a4233e1a1 [wpimath] Add script for updating Drake (#3470)
39373c6d2 Update README.md for new GCC version requirement (#3467)
d29acc90a [wpigui] Add option to reset UI on exit (#3463)
a371235b0 [ntcore] Fix dangling pointer in logger (#3465)
53b4891a5 [wpilibcintegrationtests] Fix deprecated Preferences usage (#3461)
646ded912 [wpimath] Remove incorrect discretization in pose estimators (#3460)
ea0b8f48e Fix some deprecation warnings due to fmtlib upgrade (#3459)
2067d7e30 [wpilibjexamples] Add wpimathjni, wpiutiljni to library path (#3455)
866571ab4 [wpiutil] Upgrade to fmtlib 8.0.0 (#3457)
4e1fa0308 [build] Skip PDB copy on windows build servers (#3458)
b45572167 [build] Change CI back to 18.04 docker images (#3456)
57a160f1b [wpilibc] Fix LiveWindow deprecation warning in RobotBase skeleton template (#3454)
29ae8640d [HLT] Implement duty cycle cross connect tests (#3453)
ee6377e54 [HLT] Add relay and analog cross connects (#3452)
b0f1ae7ea [build] CMake: Build the HAL even if WITH_CSCORE=OFF (#3449)
7aae2b72d Replace std::to_string() with fmt::format() (#3451)
73fcbbd74 [HLT] Add relay digital cross connect tests (#3450)
e7bedde83 [HLT] Add PWM tests that use DMA as the back end (#3447)
7253edb1e [wpilibc] Timer: Fix deprecated warning (#3446)
efa28125c [wpilibc] Add message to RobotBase on how to read stacktrace (#3444)
9832fcfe1 [hal] Fix DIO direction getter (#3445)
49c71f9f2 [wpilibj] Clarify robot quit message (#3364)
791770cf6 [wpimath] Move controller from wpilibj to wpimath (#3439)
9ce9188ff [wpimath] Add ReportWarning to MathShared (#3441)
362066a9b [wpilib] Deprecate getInstance() in favor of static functions (#3440)
26ff9371d Initial commit of cross connect integration test project (#3434)
4a36f86c8 [hal] Add support for DMA to Java (#3158)
85144e47f [commands] Unbreak build (#3438)
b417d961e Split Sendable into NT and non-NT portions (#3432)
ef4ea84cb [commands] Change grouping decorator impl to flatten nested group structures (#3335)
b422665a3 [examples] Invert right side of drive subsystems (#3437)
186dadf14 [hal] Error if attempting to set DIO output on an input port (#3436)
04e64db94 Remove redundant C++ lambda parentheses (NFC) (#3433)
f60994ad2 [wpiutil] Rename Java package to edu.wpi.first.util (#3431)
cfa1ca96f [wpilibc] Make ShuffleboardValue non-copyable (#3430)
4d9ff7643 Fix documentation warnings generated by JavaDoc (NFC) (#3428)
9e1b7e046 [build] Fix clang-tidy and clang-format (#3429)
a77c6ff3a [build] Upgrade clang-format and clang-tidy (NFC) (#3422)
099fde97d [wpilib] Improve PDP comments (NFC) (#3427)
f8fc2463e [wpilibc, wpiutil] Clean up includes (NFC) (#3426)
e246b7884 [wpimath] Clean up member initialization in feedforward classes (#3425)
c1e128bd5 Disable frivolous PMD warnings and enable PMD in ntcore (#3419)
8284075ee Run "Lint and Format" CI job on push as well as pull request (#3412)
f7db09a12 [wpimath] Move C++ filters into filter folder to match Java (#3417)
f9c3d54bd [wpimath] Reset error covariance in pose estimator ResetPosition() (#3418)
0773f4033 [hal] Ensure HAL status variables are initialized to zero (#3421)
d068fb321 [build] Upgrade CI to use 20.04 docker images (#3420)
8d054c940 [wpiutil] Remove STLExtras.h
80f1d7921 [wpiutil] Split function_ref to a separate header
64f541325 Use wpi::span instead of wpi::ArrayRef across all libraries (#3414)
2abbbd9e7 [build] clang-tidy: Remove bugprone-exception-escape (#3415)
a5c471af7 [wpimath] Add LQR template specialization for 2x2 system
edd2f0232 [wpimath] Add DARE solver for Q, R, and N with LQR ctor overloads
b2c3b2dd8 Use std::string_view and fmtlib across all libraries (#3402)
4f1cecb8e [wpiutil] Remove Path.h (#3413)
b336eac34 [build] Publish halsim_ws_core to Maven
2a09f6fa4 [build] Also build sim modules as static libraries
0e702eb79 [hal] Add a unified PCM object (#3331)
dea841103 [wpimath] Add fmtlib formatter overloads for Eigen::Matrix and units (#3409)
82856cf81 [wpiutil] Improve wpi::circular_buffer iterators (#3410)
8aecda03e [wpilib] Fix a documentation typo (#3408)
5c817082a [wpilib] Remove InterruptableSensorBase and replace with interrupt classes (#2410)
15c521a7f [wpimath] Fix drivetrain system identification (#3406)
989de4a1b [build] Force all linker warnings to be fatal for rio builds (#3407)
d9eeb45b0 [wpilibc] Add units to Ultrasonic class API (#3403)
fe570e000 [wpiutil] Replace llvm filesystem with C++17 filesystem (#3401)
01dc0249d [wpimath] Move SlewRateLimiter from wpilib to wpimath (#3399)
93523d572 [wpilibc] Clean up integration tests (#3400)
4f7a4464d [wpiutil] Rewrite StringExtras for std::string_view (#3394)
e09293a15 [wpilibc] Transition C++ classes to units::second_t (#3396)
827b17a52 [build] Create run tasks for Glass and OutlineViewer (#3397)
a61037996 [wpiutil] Avoid MSVC warning on span include (#3393)
4e2c3051b [wpilibc] Use std::string_view instead of Twine (#3380)
50915cb7e [wpilibc] MotorSafety::GetDescription(): Return std::string (#3390)
f4e2d26d5 [wpilibc] Move NullDeleter from frc/Base.h to wpi/NullDeleter.h (#3387)
cb0051ae6 [wpilibc] SimDeviceSim: use fmtlib (#3389)
a238cec12 [wpiutil] Deprecate wpi::math constants in favor of wpi::numbers (#3383)
393bf23c0 [ntcore, cscore, wpiutil] Standardize template impl files on .inc extension (NFC) (#3124)
e7d9ba135 [sim] Disable flaky web server integration tests (#3388)
0a0003c11 [wpilibjExamples] Fix name of Java swerve drive pose estimator example (#3382)
7e1b27554 [wpilibc] Use default copies and moves when possible (#3381)
fb2a56e2d [wpilibc] Remove START_ROBOT_CLASS macro (#3384)
84218bfb4 [wpilibc] Remove frc namespace shim (#3385)
dd7824340 [wpilibc] Remove C++ compiler version static asserts (#3386)
484cf9c0e [wpimath] Suppress the -Wmaybe-uninitialized warning in Eigen (#3378)
a04d1b4f9 [wpilibc] DriverStation: Remove ReportError and ReportWarning
831c10bdf [wpilibc] Errors: Use fmtlib
87603e400 [wpiutil] Import fmtlib (#3375)
442621672 [wpiutil] Add ArrayRef/std::span/wpi::span implicit conversions
bc15b953b [wpiutil] Add std::span implementation
6d20b1204 [wpiutil] StringRef, Twine, raw_ostream: Add std::string_view support (#3373)
2385c2a43 [wpilibc] Remove Utility.h (#3376)
87384ea68 [wpilib] Fix PIDController continuous range error calculations (#3170)
04dae799a [wpimath] Add SimpleMotorFeedforward::Calculate(velocity, nextVelocity) overload (#3183)
0768c3903 [wpilib] DifferentialDrive: Remove right side inversion (#3340)
8dd8d4d2d [wpimath] Fix redundant nested math package introduced by #3316 (#3368)
49b06beed [examples] Add Field2d to RamseteController example (#3371)
4c562a445 [wpimath] Fix typo in comment of update_eigen.py (#3369)
fdbbf1188 [wpimath] Add script for updating Eigen
f1e64b349 [wpimath] Move Eigen unsupported folder into eigeninclude
224f3a05c [sim] Fix build error when building with GCC 11.1 (#3361)
ff56d6861 [wpilibj] Fix SpeedController deprecated warnings (#3360)
1873fbefb [examples] Fix Swerve and Mecanum examples (#3359)
80b479e50 [examples] Fix SwerveBot example to use unique encoder ports (#3358)
1f7c9adee [wpilibjExamples] Fix pose estimator examples (#3356)
9ebc3b058 [outlineviewer] Change default size to 600x400 (#3353)
e21b443a4 [build] Gradle: Make C++ examples runnable (#3348)
da590120c [wpilibj] Add MotorController.setVoltage default (#3347)
561d53885 [build] Update opencv to 4.5.2, imgui/implot to latest (#3344)
44ad67ca8 [wpilibj] Preferences: Add missing Deprecated annotation (#3343)
3fe8fc75a [wpilibc] Revert "Return reference from GetInstance" (#3342)
3cc2da332 Merge branch '2022'
a3cd90dd7 [wpimath] Fix classpath used by generate_numbers.py (#3339)
d6cfdd3ba [wpilib] Preferences: Deprecate Put* in favor of Set* (#3337)
ba08baabb [wpimath] Update Drake DARE solver to v0.29.0 (#3336)
497b712f6 [wpilib] Make IterativeRobotBase::m_period private with getter
f00dfed7a [wpilib] Remove IterativeRobot base class
3c0846168 [hal] Use last error reporting instead of PARAMETER_OUT_OF_RANGE (#3328)
5ef2b4fdc [wpilibj] Fix @deprecated warning for SerialPort constructor (#3329)
23d2326d1 [hal] Report previous allocation location for indexed resource duplicates (#3322)
e338f9f19 [build] Fix wpilibc runCpp task (#3327)
c8ff626fe [wpimath] Move Java classes to edu.wpi.first.math (#3316)
4e424d51f [wpilibj] DifferentialDrivetrainSim: Rename constants to match the style guide (#3312)
6b50323b0 [cscore] Use Lock2DSize if possible for Windows USB cameras (#3326)
65c148536 [wpilibc] Fix "control reaches end of non-void function" warning (#3324)
f99f62bee [wpiutil] uv Handle: Use malloc/free instead of new/delete (#3325)
365f5449c [wpimath] Fix MecanumDriveKinematics (#3266)
ff52f207c [glass, wpilib] Rewrite Mechanism2d (#3281)
ee0eed143 [wpimath] Add DCMotor factory function for Romi motors (#3319)
512738072 [hal] Add HAL_GetLastError to enable better error messages from HAL calls (#3320)
ced654880 [glass, outlineviewer] Update Mac icons to macOS 11 style (#3313)
936d3b9f8 [templates] Add Java template for educational robot (#3309)
6e31230ad [examples] Fix odometry update in SwerveControllerCommand example (#3310)
05ebe9318 Merge branch 'main' into 2022
aaf24e255 [wpilib] Fix initial heading behavior in HolonomicDriveController (#3290)
8d961dfd2 [wpilibc] Remove ErrorBase (#3306)
659b37ef9 [wpiutil] StackTrace: Include offset on Linux (#3305)
0abf6c904 [wpilib] Move motor controllers to motorcontrol package (#3302)
4630191fa [wpiutil] circular_buffer: Use value initialization instead of passing zero (#3303)
b7b178f49 [wpilib] Remove Potentiometer interface
687066af3 [wpilib] Remove GyroBase
6b168ab0c [wpilib] Remove PIDController, PIDOutput, PIDSource
948625de9 [wpimath] Document conversion from filter cutoff frequency to time constant (#3299)
3848eb8b1 [wpilibc] Fix flywhel -> flywheel typo in FlywheelSim (#3298)
3abe0b9d4 [cscore] Move java package to edu.wpi.first.cscore (#3294)
d7fabe81f [wpilib] Remove RobotDrive (#3295)
1dc81669c [wpilib] Remove GearTooth (#3293)
01d0e1260 [wpilib] Revert move of RomiGyro into main wpilibc/j (#3296)
397e569aa [ntcore] Remove "using wpi" from nt namespace
79267f9e6 [ntcore] Remove NetworkTable -> nt::NetworkTable shim
48ebe5736 [ntcore] Remove deprecated Java interfaces and classes
c2064c78b [ntcore] Remove deprecated ITable interfaces
36608a283 [ntcore] Remove deprecated C++ APIs
a1c87e1e1 [glass] LogView: Add "copy to clipboard" button (#3274)
fa7240a50 [wpimath] Fix typo in quintic spline basis matrix
ffb4d38e2 [wpimath] Add derivation for spline basis matrices
f57c188f2 [wpilib] Add AnalogEncoder(int) ctor (#3273)
8471c4fb2 [wpilib] FieldObject2d: Add setTrajectory() method (#3277)
c97acd18e [glass] Field2d enhancements (#3234)
ffb590bfc [wpilib] Fix Compressor sendable properties (#3269)
6137f98eb [hal] Rename SimValueCallback2 to SimValueCallback (#3212)
a6f653969 [hal] Move registerSimPeriodic functions to HAL package (#3211)
10c038d9b [glass] Plot: Fix window creation after removal (#3264)
2d2eaa3ef [wpigui] Ensure window will be initially visible (#3256)
4d28b1f0c [wpimath] Use JNI for trajectory serialization (#3257)
3de800a60 [wpimath] TrajectoryUtil.h: Comment formatting (NFC) (#3262)
eff592377 [glass] Plot: Don't overwrite series ID (#3260)
a79faace1 [wpilibc] Return reference from GetInstance (#3247)
9550777b9 [wpilib] PWMSpeedController: Use PWM by composition (#3248)
c8521a3c3 [glass] Plot: Set reasonable default window size (#3261)
d71eb2cf3 [glass] Plot: Show full source name as tooltip and in popup (#3255)
160fb740f [hal] Use std::lround() instead of adding 0.5 and truncating (#3012)
48e9f3951 [wpilibj] Remove wpilibj package CameraServer (#3213)
8afa596fd [wpilib] Remove deprecated Sendable functions and SendableBase (#3210)
d3e45c297 [wpimath] Make C++ geometry classes immutable (#3249)
2c98939c1 [glass] StringChooser: Don't call SameLine() at end
a18a7409f [glass] NTStringChooser: Clear value of deleted entries
2f19cf452 [glass] NetworkTablesHelper: listen to delete events
da96707dc Merge branch 'main' into 2022
c3a8bdc24 [build] Fix clang-tidy action (#3246)
21624ef27 Add ImGui OutlineViewer (#3220)
1032c9b91 [wpiutil] Unbreak wpi::Format on Windows (#3242)
2e07902d7 [glass] NTField2D: Fix name lookup (#3233)
6e23e1840 [wpilibc] Remove WPILib.h (#3235)
3e22e4506 [wpilib] Make KoP drivetrain simulation weight 60 lbs (#3228)
79d1bd6c8 [glass] NetworkTablesSetting: Allow disable of server option (#3227)
fe341a16f [examples] Use more logical elevator setpoints in GearsBot (#3198)
62abf46b3 [glass] NetworkTablesSettings: Don't block GUI (#3226)
a95a5e0d9 [glass] Move NetworkTablesSettings to libglassnt (#3224)
d6f6ceaba [build] Run Spotless formatter (NFC) (#3221)
0922f8af5 [commands] CommandScheduler.requiring(): Note return can be null (NFC) (#2934)
6812302ff [examples] Make DriveDistanceOffboard example work in sim (#3199)
f3f86b8e7 [wpimath] Add pose estimator overload for vision + std dev measurement (#3200)
1a2680b9e [wpilibj] Change CommandBase.withName() to return CommandBase (#3209)
435bbb6a8 [command] RamseteCommand: Output 0 if interrupted (#3216)
3cf44e0a5 [hal] Add function for changing HAL Notifier thread priority (#3218)
40b367513 [wpimath] Units.java: Add kg-lb conversions (#3203)
9f563d584 [glass] NT: Fix return value in StringToDoubleArray (#3208)
af4adf537 [glass] Auto-size plots to fit window (#3193)
2560146da [sim] GUI: Add option to show prefix in Other Devices (#3186)
eae3a6397 gitignore: Ignore .cache directory (#3196)
959611420 [wpilib] Require non-zero positive value for PIDController.period (#3175)
9522f2e8c [wpimath] Add methods to concatenate trajectories (#3139)
e42a0b6cf [wpimath] Rotation2d comment formatting (NFC) (#3162)
d1c7032de [wpimath] Fix order of setting gyro offset in pose estimators (#3176)
d241bc81a [sim] Add DoubleSolenoidSim and SolenoidSim classes (#3177)
cb7f39afa [wpilibc] Add RobotController::GetBatteryVoltage() to C++ (#3179)
99b5ad9eb [wpilibj] Fix warnings that are not unused variables or deprecation (#3161)
c14b23775 [build] Fixup doxygen generated include dirs to match what users would need (#3154)
d447c7dc3 [sim] Add SimDeviceSim ctor overloads (#3134)
247420c9c [build] Remove jcenter repo (#3157)
04b112e00 [build] Include debug info in plugin published artifacts (#3149)
be0ce9900 [examples] Use PWMSparkMax instead of PWMVictorSPX (#3156)
69e8d0b65 [wpilib] Move RomiGyro into main wpilibc/j (#3143)
94e685e1b [wpimath] Add custom residual support to EKF (#3148)
5899f3dd2 [sim] GUI: Make keyboard settings loading more robust (#3167)
f82aa1d56 [wpilib] Fix HolonomicDriveController atReference() behavior (#3163)
fe5c2cf4b [wpimath] Remove ControllerUtil.java (#3169)
43d40c6e9 [wpiutil] Suppress unchecked cast in CombinedRuntimeLoader (#3155)
3d44d8f79 [wpimath] Fix argument order in UKF docs (NFC) (#3147)
ba6fe8ff2 [cscore] Add USB camera change event (#3123)
533725888 [build] Tweak OpenCV cmake search paths to work better on Linux (#3144)
29bf9d6ef [cscore] Add polled support to listener
483beb636 [ntcore] Move CallbackManager to wpiutil
fdaec7759 [examples] Instantiate m_ramseteController in example (#3142)
8494a5761 Rename default branch to main (#3140)
45590eea2 [wpigui] Hardcode window scale to 1 on macOS (#3135)
834a64920 [build] Publish libglass and libglassnt to Maven (#3127)
2c2ccb361 [wpimath] Fix Rotation2d equality operator (#3128)
fb5c8c39a [wpigui] clang-tidy: readability-braces-around-statements
f7d39193a [wpigui] Fix copyright in pfd and wpigui_metal.mm
aec796b21 [ntcore] Fix conditional jump on uninitialized value (#3125)
fb13bb239 [sim] GUI: Add right click popup for keyboard joystick settings (#3119)
c517ec677 [build] Update thirdparty-imgui to 1.79-2 (#3118)
e8cbf2a71 [wpimath] Fix typo in SwerveDrivePoseEstimator doc (NFC) (#3112)
e9c86df46 [wpimath] Add tests for swerve module optimization (#3100)
6ba8c289c [examples] Remove negative of ArcadeDrive(fwd, ..) in the C++ Getting Started Example (#3102)
3f1672e89 [hal] Add SimDevice createInt() and createLong() (#3110)
15be5cbf1 [examples] Fix segfault in GearsBot C++ example (#3111)
4cf0e5e6d Add quick links to API documentation in README (#3082)
6b1898f12 Fix RT priority docs (NFC) (#3098)
b3426e9c0 [wpimath] Fix missing whitespace in pose estimator doc (#3097)
38c1a1f3e [examples] Fix feildRelative -> fieldRelative typo in XControllerCommand examples (#3104)
4488e25f1 [glass] Shorten SmartDashboard window names (#3096)
cfdb3058e [wpilibj] Update SimDeviceSimTest (#3095)
64adff5fe [examples] Fix typo in ArcadeDrive constructor parameter name (#3092)
6efc58e3d [build] Fix issues with build on windows, deprecations, and native utils (#3090)
f393989a5 [wpimath, wpiutil] Add wpi::array for compile time size checking (#3087)
d6ed20c1e [build] Set macOS deployment target to 10.14 (#3088)
7c524014c [hal] Add [[nodiscard]] to HAL_WaitForNotifierAlarm() (#3085)
406d055f0 [wpilib] Fixup wouldHitLowerLimit in elevator and arm simulation classes. (#3076)
04a90b5dd [examples] Don't continually set setpoint in PotentiometerPID Examples (#3084)
8c5bfa013 [sim] GUI: Add max value setting for keyboard joysticks (#3083)
bc80c5535 [hal] Add SimValue reset() function (#3064)
9c3b51ca0 [wpilib] Document simulation APIs (#3079)
26584ff14 [wpimath] Add model description to LinearSystemId Javadocs (#3080)
42c3d5286 [examples] Sync Java and C++ trajectories in sim example (#3081)
64e72f710 [wpilibc] Add missing function RoboRioSim::ResetData (#3073)
e95503798 [wpimath] Add optimize() to SwerveModuleState (#3065)
fb99910c2 [hal] Add SimInt and SimLong wrappers for int/long SimValue (#3066)
e620bd4d3 [doc] Add machine-readable websocket specification (#3059)
a44e761d9 [glass] Add support for plot Y axis labels
ea1974d57 [wpigui] Update imgui and implot to latest
85a0bd43c [wpimath] Add RKF45 integration (#3047)
278e0f126 [glass] Use .controllable to set widgets' read-only state (#3035)
d8652cfd4 [wpimath] Make Java DCMotor API consistent with C++ and fix motor calcs (#3046)
377b7065a [build] Add toggleOffOn to Java spotless (#3053)
1e9c79c58 [sim] Use plant output to retrieve simulated position (#3043)
78147aa34 [sim] GUI: Fix Keyboard Joystick (#3052)
cd4a2265b [ntcore] Fix NetworkTableEntry::GetRaw() (#3051)
767ac1de1 [build] Use deploy key for doc publish (#3048)
d762215d1 [build] Add publish documentation script (#3040)
1fd09593c [examples] Add missing TestInit method to GettingStarted Example (#3039)
e45a0f6ce [examples] Add RomiGyro to the Romi Reference example (#3037)
94f852572 Update imaging link and fix typo (#3038)
d73cf64e5 [examples] Update RomiReference to match motor directions (#3036)
f945462ba Bump copyright year to 2021 (#3033)
b05946175 [wpimath] Catch Drake JNI exceptions and rethrow them (#3032)
62f0f8190 [wpimath] Deduplicate angle modulus functions (#2998)
bf8c0da4b [glass] Add "About" popup with version number (#3031)
dfdd6b389 [build] Increase Gradle heap size in Gazebo build (#3028)
f5e0fc3e9 Finish clang-tidy cleanups (#3003)
d741101fe [sim] Revert accidental commit of WSProvider_PDP.h (#3027)
e1620799c [examples] Add C++ RomiReference example (#2969)
749c7adb1 [command] Fix use-after-free in CommandScheduler (#3024)
921a73391 [sim] Add WS providers for AddressableLED, PCM, and Solenoid (#3026)
26d0004fe [build] Split Actions into different yml files (#3025)
948af6d5b [wpilib] PWMSpeedController.get(): Apply Inversion (#3016)
670a187a3 [wpilibc] SuppliedValueWidget.h: Forward declare ShuffleboardContainer (#3021)
be9f72502 [ntcore] NetworkTableValue: Use std::forward instead of std::move (#3022)
daf3f4cb1 [cscore] cscore_raw_cv.h: Fix error in PutFrame() (#3019)
5acda4cc7 [wpimath] ElevatorFeedforward.h: Add time.h include
8452af606 [wpimath] units/radiation.h: Add mass.h include
630d44952 [hal] ErrorsInternal.h: Add stdint.h include
7372cf7d9 [cscore] Windows NetworkUtil.cpp: Add missing include
b7e46c558 Include .h from .inc/.inl files (NFC) (#3017)
bf8f8710e [examples] Update Romi template and example (#2996)
6ffe5b775 [glass] Ensure NetworkTableTree parent context menu has an id (#3015)
be0805b85 [build] Update to WPILibVersioningPlugin 4.1.0 (#3014)
65b2359b2 [build] Add spotless for other files (#3007)
8651aa73e [examples] Enable NT Flush in Field2d examples (#3013)
78b542737 [build] Add Gazebo build to Actions CI (#3004)
fccf86532 [sim] DriverStationGui: Fix two bugs (#3010)
185741760 [sim] WSProvider_Joystick: Fix off-by-1 in incoming buttons (#3011)
ee7114a58 [glass] Add drive class widgets (#2975)
00fa91d0d [glass] Use ImGui style for gyro widget colors (#3009)
b7a25bfc3 ThirdPartyNotices: Add portable file dialogs license (#3005)
a2e46b9a1 [glass] modernize-use-nullptr (NFC) (#3006)
a751fa22d [build] Apply spotless for java formatting (#1768)
e563a0b7d [wpimath] Make LinearSystemLoop move-constructible and move-assignable (#2967)
49085ca94 [glass] Add context menus to remove and add NetworkTables values (#2979)
560a850a2 [glass] Add NetworkTables Log window (#2997)
66782e231 [sim] Create Left/Right drivetrain current accessors (#3001)
b60eb1544 clang-tidy: bugprone-virtual-near-miss
cbe59fa3b clang-tidy: google-explicit-constructor
c97c6dc06 clang-tidy: google-readability-casting (NFC)
32fa97d68 clang-tidy: modernize-use-nullptr (NFC)
aee460326 clang-tidy: modernize-pass-by-value
29c7da5f1 clang-tidy: modernize-make-unique
6131f4e32 clang-tidy: modernize-concat-nested-namespaces (NFC)
67e03e625 clang-tidy: modernize-use-equals-default
b124f9101 clang-tidy: modernize-use-default-member-init
d11a3a638 clang-tidy: modernize-use-override (NFC)
4cc0706b0 clang-tidy: modernize-use-using (NFC)
885f5a978 [wpilibc] Speed up ScopedTracerTest (#2999)
60b596457 [wpilibj] Fix typos (NFC) (#3000)
6e1919414 [build] Bring naming checkstyle rules up to date with Google Style guide (#1781)
8c8ec5e63 [wpilibj] Suppress unchecked cast warnings (#2995)
b8413ddd5 [wpiutil] Add noexcept to timestamp static functions (#2994)
5d976b6e1 [glass] Load NetworkTableView settings on first draw (#2993)
2b4317452 Replace NOLINT(runtime/explicit) comments with NOLINT (NFC) (#2992)
1c3011ba4 [glass] Fix handling of "/" NetworkTables key (#2991)
574a42f3b [hal] Fix UnsafeManipulateDIO status check (#2987)
9005cd59e [wpilib] Clamp input voltage in sim classes (#2955)
dd494d4ab [glass] NetworkTablesModel::Update(): Avoid use-after-move (#2988)
7cca469a1 [wpimath] NormalizeAngle: Make inline, remove unnamed namespace (#2986)
2aed432b4 Add braces to C++ single-line loops and conditionals (NFC) (#2973)
0291a3ff5 [wpiutil] StringRef: Add noexcept to several constructors (#2984)
5d7315280 [wpimath] Update UnitsTest.cpp copyright (#2985)
254931b9a [wpimath] Remove LinearSystem from LinearSystemLoop (#2968)
aa89744c9 Update OtherVersions.md to include wpimath info (#2983)
1cda3f5ad [glass] Fix styleguide (#2976)
8f1f64ffb Remove year from file copyright message (NFC) (#2972)
2bc0a7795 [examples] Fix wpiformat warning about utility include (#2971)
4204da6ad [glass] Add application icon
7ac39b10f [wpigui] Add icon support
6b567e006 [wpimath] Add support for varying vision standard deviations in pose estimators (#2956)
df299d6ed [wpimath] Add UnscentedKalmanFilter::Correct() overload (#2966)
4e34f0523 [examples] Use ADXRS450_GyroSim class in simulation example (#2964)
9962f6fd7 [wpilib] Give Field2d a default Sendable name (#2953)
f9d492f4b [sim] GUI: Show "Other Devices" window by default (#2961)
a8bb2ef1c [sim] Fix ADXRS450_GyroSim and DutyCycleEncoderSim (#2963)
240c629cd [sim] Try to guess "Map Gamepad" setting (#2960)
952567dd3 [wpilibc] Add missing move constructors and assignment operators (#2959)
10b396b4c [sim] Various WebSockets fixes and enhancements (#2952)
699bbe21a [examples] Fix comments in Gearsbot to match implementation (NFC) (#2957)
27b67deca [glass] Add more widgets (#2947)
581b7ec55 [wpilib] Add option to flush NetworkTables every iterative loop
acfbb1a44 [ntcore] DispatcherBase::Flush: Use wpi::Now()
d85a6d8fe [ntcore] Reduce limit on flush and update rate to 5 ms
20fbb5c63 [sim] Fix stringop truncation warning from GCC 10 (#2945)
1051a06a7 [glass] Show NT timestamps in seconds (#2944)
98dfc2620 [glass] Fix plots (#2943)
1ba0a2ced [sim] GUI: Add keyboard virtual joystick support (#2940)
4afb13f98 [examples] Replace M_PI with wpi::math::pi (#2938)
b27d33675 [examples] Enhance Romi templates (#2931)
00b9ae77f [sim] Change default WS port number to 3300 (#2932)
65219f309 [examples] Update Field2d position in periodic() (#2928)
f78d1d434 [sim] Process WS Encoder reset internally (#2927)
941edca59 [hal] Add Java SimDeviceDataJNI.getSimDeviceName (#2924)
a699435ed [wpilibj] Fix FlywheelSim argument order in constructor (#2922)
66d641718 [examples] Add tasks to run Java examples (#2920)
558e37c41 [examples] Add simple differential drive simulation example (#2918)
4f40d991e [glass] Switch name of Glass back to glass (#2919)
549af9900 [build] Update native-utils to 2021.0.6 (#2914)
b33693009 [glass] Change basename of glass to Glass (#2915)
c9a0edfb8 [glass] Package macOS application bundle
2c5668af4 [wpigui] Add platform-specific preferences save
751dea32a [wpilibc] Try to work around ABI break introduced in #2901 (#2917)
cd8f4bfb1 [build] Package up msvc runtime into maven artifact (#2913)
a6cfcc686 [wpilibc] Move SendableChooser Doxygen comments to header (NFC) (#2911)
b8c4f603d [wpimath] Upgrade to Eigen 3.3.9 (#2910)
0075e4b39 [wpilibj] Fix NPE in Field2d (#2909)
125af556c [simulation] Fix halsim_gui ntcore and wpiutil deps (#2908)
963ad5c25 [wpilib] Add noise to Differential Drive simulator (#2903)
387f56cb7 [examples] Add Romi reference Java example and templates (#2905)
b3deda38c [examples] Zero motors on disabledInit() in sim physics examples (#2906)
2a5ca7745 [glass] Add glass: an application for display of robot data
727940d84 [wpilib] Move Field2d to SmartDashboard
8cd42478e [wpilib] SendableBuilder: Make GetTable() visible
c11d34b26 [command] Use addCommands in command group templates (#2900)
339d7445b [sim] Add HAL hooks for simulationPeriodic (#2881)
d16f05f2c [wpilib] Fix SmartDashboard update order (#2896)
5427b32a4 [wpiutil] unique_function: Restrict implicit conversion (#2899)
f73701239 [ntcore] Add missing SetDefault initializer_list functions (#2898)
f5a6fc070 [sim] Add initialized flag for all solenoids on a PCM (#2897)
bdf5ba91a [wpilibj] Fix typo in ElevatorSim (#2895)
bc8f33877 [wpilib] Add pose estimators (#2867)
3413bfc06 [wpilib] PIDController: Recompute the error in AtSetpoint() (#2822)
2056f0ce0 [wpilib] Fix bugs in Hatchbot examples (#2893)
5eb8cfd69 [wpilibc] Fix MatchDataSender (#2892)
e6a425448 [build] Delete test folders after tests execute (#2891)
d478ad00d [imgui] Allow usage of imgui_stdlib (#2889)
53eda861d [build] Add unit-testing infrastructure to examples (#2863)
cc1d86ba6 [sim] Add title to simulator GUI window (#2888)
f0528f00e [build] CMake: Use project-specific binary and source dirs (#2886)
5cd2ad124 [wpilibc] Add Color::operator!= (#2887)
6c00e7a90 [build] CI CMake: build with GUI enabled (#2884)
53170bbb5 Update roboRIO toolchain installation instructions (#2883)
467258e05 [sim] GUI: Add option to not zero disconnected joysticks (#2876)
129be23c9 Clarify JDK installation instructions in readme (#2882)
8e9290e86 [build] Add separate CMake setting for wpimath (#2885)
7cf5bebf8 [wpilibj] Cache NT writes from DriverStation (#2780)
f7f9087fb [command] Fix timing issue in RamseteCommand (#2871)
256e7904f [wpilibj] SimDeviceSim: Fix sim value changed callback (#2880)
c8ea1b6c3 [wpilib] Add function to adjust LQR controller gain for pure time delay (#2878)
2816b06c0 [sim] HAL_GetControlWord: Fully zero output (#2873)
4c695ea08 Add toolchain installation instructions to README (#2875)
a14d51806 [wpimath] DCMotor: fix doc typo (NFC) (#2868)
017097791 [build] CMake: build sim extensions as shared libs (#2866)
f61726b5a [build] Fix cmake-config files (#2865)
fc27fdac5 [wpilibc] Cache NT values from driver station (#2768)
47c59859e [sim] Make SimDevice callbacks synchronous (#2861)
6e76ab9c0 [build] Turn on WITH_GUI for Windows cmake CI
5f78b7670 [build] Set GLFW_INSTALL to OFF
5e0808c84 [wpigui] Fix Windows cmake build
508f05a47 [imgui] Fix typo in Windows CMake target sources

Change-Id: I1737b45965f31803a96676bedc7dc40e337aa321
git-subtree-dir: third_party/allwpilib
git-subtree-split: e473a00f9785f9949e5ced30901baeaf426d2fc9
Signed-off-by: Austin Schuh <austin.linux@gmail.com>
diff --git a/wpilibc/src/main/native/cpp/ADXL345_I2C.cpp b/wpilibc/src/main/native/cpp/ADXL345_I2C.cpp
index aff0802..484e690 100644
--- a/wpilibc/src/main/native/cpp/ADXL345_I2C.cpp
+++ b/wpilibc/src/main/native/cpp/ADXL345_I2C.cpp
@@ -1,28 +1,25 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/ADXL345_I2C.h"
 
 #include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 using namespace frc;
 
 ADXL345_I2C::ADXL345_I2C(I2C::Port port, Range range, int deviceAddress)
     : m_i2c(port, deviceAddress),
-      m_simDevice("ADXL345_I2C", port, deviceAddress) {
+      m_simDevice("Accel:ADXL345_I2C", port, deviceAddress) {
   if (m_simDevice) {
-    m_simRange =
-        m_simDevice.CreateEnum("Range", true, {"2G", "4G", "8G", "16G"}, 0);
-    m_simX = m_simDevice.CreateDouble("X Accel", false, 0.0);
-    m_simY = m_simDevice.CreateDouble("Y Accel", false, 0.0);
-    m_simZ = m_simDevice.CreateDouble("Z Accel", false, 0.0);
+    m_simRange = m_simDevice.CreateEnumDouble("range", hal::SimDevice::kOutput,
+                                              {"2G", "4G", "8G", "16G"},
+                                              {2.0, 4.0, 8.0, 16.0}, 0);
+    m_simX = m_simDevice.CreateDouble("x", hal::SimDevice::kInput, 0.0);
+    m_simY = m_simDevice.CreateDouble("y", hal::SimDevice::kInput, 0.0);
+    m_simZ = m_simDevice.CreateDouble("z", hal::SimDevice::kInput, 0.0);
   }
   // Turn on the measurements
   m_i2c.Write(kPowerCtlRegister, kPowerCtl_Measure);
@@ -32,7 +29,15 @@
   HAL_Report(HALUsageReporting::kResourceType_ADXL345,
              HALUsageReporting::kADXL345_I2C, 0);
 
-  SendableRegistry::GetInstance().AddLW(this, "ADXL345_I2C", port);
+  wpi::SendableRegistry::AddLW(this, "ADXL345_I2C", port);
+}
+
+I2C::Port ADXL345_I2C::GetI2CPort() const {
+  return m_i2c.GetPort();
+}
+
+int ADXL345_I2C::GetI2CDeviceAddress() const {
+  return m_i2c.GetDeviceAddress();
 }
 
 void ADXL345_I2C::SetRange(Range range) {
@@ -40,16 +45,28 @@
               kDataFormat_FullRes | static_cast<uint8_t>(range));
 }
 
-double ADXL345_I2C::GetX() { return GetAcceleration(kAxis_X); }
+double ADXL345_I2C::GetX() {
+  return GetAcceleration(kAxis_X);
+}
 
-double ADXL345_I2C::GetY() { return GetAcceleration(kAxis_Y); }
+double ADXL345_I2C::GetY() {
+  return GetAcceleration(kAxis_Y);
+}
 
-double ADXL345_I2C::GetZ() { return GetAcceleration(kAxis_Z); }
+double ADXL345_I2C::GetZ() {
+  return GetAcceleration(kAxis_Z);
+}
 
 double ADXL345_I2C::GetAcceleration(ADXL345_I2C::Axes axis) {
-  if (axis == kAxis_X && m_simX) return m_simX.Get();
-  if (axis == kAxis_Y && m_simY) return m_simY.Get();
-  if (axis == kAxis_Z && m_simZ) return m_simZ.Get();
+  if (axis == kAxis_X && m_simX) {
+    return m_simX.Get();
+  }
+  if (axis == kAxis_Y && m_simY) {
+    return m_simY.Get();
+  }
+  if (axis == kAxis_Z && m_simZ) {
+    return m_simZ.Get();
+  }
   int16_t rawAccel = 0;
   m_i2c.Read(kDataRegister + static_cast<int>(axis), sizeof(rawAccel),
              reinterpret_cast<uint8_t*>(&rawAccel));
@@ -74,12 +91,12 @@
   return data;
 }
 
-void ADXL345_I2C::InitSendable(SendableBuilder& builder) {
+void ADXL345_I2C::InitSendable(nt::NTSendableBuilder& builder) {
   builder.SetSmartDashboardType("3AxisAccelerometer");
   auto x = builder.GetEntry("X").GetHandle();
   auto y = builder.GetEntry("Y").GetHandle();
   auto z = builder.GetEntry("Z").GetHandle();
-  builder.SetUpdateTable([=]() {
+  builder.SetUpdateTable([=] {
     auto data = GetAccelerations();
     nt::NetworkTableEntry(x).SetDouble(data.XAxis);
     nt::NetworkTableEntry(y).SetDouble(data.YAxis);
diff --git a/wpilibc/src/main/native/cpp/ADXL345_SPI.cpp b/wpilibc/src/main/native/cpp/ADXL345_SPI.cpp
index 329b148..9a95bcc 100644
--- a/wpilibc/src/main/native/cpp/ADXL345_SPI.cpp
+++ b/wpilibc/src/main/native/cpp/ADXL345_SPI.cpp
@@ -1,27 +1,24 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/ADXL345_SPI.h"
 
 #include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 using namespace frc;
 
 ADXL345_SPI::ADXL345_SPI(SPI::Port port, ADXL345_SPI::Range range)
-    : m_spi(port), m_simDevice("ADXL345_SPI", port) {
+    : m_spi(port), m_simDevice("Accel:ADXL345_SPI", port) {
   if (m_simDevice) {
-    m_simRange =
-        m_simDevice.CreateEnum("Range", true, {"2G", "4G", "8G", "16G"}, 0);
-    m_simX = m_simDevice.CreateDouble("X Accel", false, 0.0);
-    m_simY = m_simDevice.CreateDouble("Y Accel", false, 0.0);
-    m_simZ = m_simDevice.CreateDouble("Z Accel", false, 0.0);
+    m_simRange = m_simDevice.CreateEnumDouble("range", hal::SimDevice::kOutput,
+                                              {"2G", "4G", "8G", "16G"},
+                                              {2.0, 4.0, 8.0, 16.0}, 0);
+    m_simX = m_simDevice.CreateDouble("x", hal::SimDevice::kInput, 0.0);
+    m_simY = m_simDevice.CreateDouble("y", hal::SimDevice::kInput, 0.0);
+    m_simZ = m_simDevice.CreateDouble("z", hal::SimDevice::kInput, 0.0);
   }
   m_spi.SetClockRate(500000);
   m_spi.SetMSBFirst();
@@ -40,7 +37,11 @@
   HAL_Report(HALUsageReporting::kResourceType_ADXL345,
              HALUsageReporting::kADXL345_SPI);
 
-  SendableRegistry::GetInstance().AddLW(this, "ADXL345_SPI", port);
+  wpi::SendableRegistry::AddLW(this, "ADXL345_SPI", port);
+}
+
+SPI::Port ADXL345_SPI::GetSpiPort() const {
+  return m_spi.GetPort();
 }
 
 void ADXL345_SPI::SetRange(Range range) {
@@ -51,19 +52,33 @@
   commands[1] = kDataFormat_FullRes | static_cast<uint8_t>(range & 0x03);
   m_spi.Transaction(commands, commands, 2);
 
-  if (m_simRange) m_simRange.Set(range);
+  if (m_simRange) {
+    m_simRange.Set(range);
+  }
 }
 
-double ADXL345_SPI::GetX() { return GetAcceleration(kAxis_X); }
+double ADXL345_SPI::GetX() {
+  return GetAcceleration(kAxis_X);
+}
 
-double ADXL345_SPI::GetY() { return GetAcceleration(kAxis_Y); }
+double ADXL345_SPI::GetY() {
+  return GetAcceleration(kAxis_Y);
+}
 
-double ADXL345_SPI::GetZ() { return GetAcceleration(kAxis_Z); }
+double ADXL345_SPI::GetZ() {
+  return GetAcceleration(kAxis_Z);
+}
 
 double ADXL345_SPI::GetAcceleration(ADXL345_SPI::Axes axis) {
-  if (axis == kAxis_X && m_simX) return m_simX.Get();
-  if (axis == kAxis_Y && m_simY) return m_simY.Get();
-  if (axis == kAxis_Z && m_simZ) return m_simZ.Get();
+  if (axis == kAxis_X && m_simX) {
+    return m_simX.Get();
+  }
+  if (axis == kAxis_Y && m_simY) {
+    return m_simY.Get();
+  }
+  if (axis == kAxis_Z && m_simZ) {
+    return m_simZ.Get();
+  }
   uint8_t buffer[3];
   uint8_t command[3] = {0, 0, 0};
   command[0] = (kAddress_Read | kAddress_MultiByte | kDataRegister) +
@@ -103,12 +118,12 @@
   return data;
 }
 
-void ADXL345_SPI::InitSendable(SendableBuilder& builder) {
+void ADXL345_SPI::InitSendable(nt::NTSendableBuilder& builder) {
   builder.SetSmartDashboardType("3AxisAccelerometer");
   auto x = builder.GetEntry("X").GetHandle();
   auto y = builder.GetEntry("Y").GetHandle();
   auto z = builder.GetEntry("Z").GetHandle();
-  builder.SetUpdateTable([=]() {
+  builder.SetUpdateTable([=] {
     auto data = GetAccelerations();
     nt::NetworkTableEntry(x).SetDouble(data.XAxis);
     nt::NetworkTableEntry(y).SetDouble(data.YAxis);
diff --git a/wpilibc/src/main/native/cpp/ADXL362.cpp b/wpilibc/src/main/native/cpp/ADXL362.cpp
index aff572f..deda965 100644
--- a/wpilibc/src/main/native/cpp/ADXL362.cpp
+++ b/wpilibc/src/main/native/cpp/ADXL362.cpp
@@ -1,17 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/ADXL362.h"
 
 #include <hal/FRCUsageReporting.h>
+#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-#include "frc/DriverStation.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
@@ -35,13 +32,14 @@
 ADXL362::ADXL362(Range range) : ADXL362(SPI::Port::kOnboardCS1, range) {}
 
 ADXL362::ADXL362(SPI::Port port, Range range)
-    : m_spi(port), m_simDevice("ADXL362", port) {
+    : m_spi(port), m_simDevice("Accel:ADXL362", port) {
   if (m_simDevice) {
-    m_simRange =
-        m_simDevice.CreateEnum("Range", true, {"2G", "4G", "8G", "16G"}, 0);
-    m_simX = m_simDevice.CreateDouble("X Accel", false, 0.0);
-    m_simY = m_simDevice.CreateDouble("Y Accel", false, 0.0);
-    m_simZ = m_simDevice.CreateDouble("Z Accel", false, 0.0);
+    m_simRange = m_simDevice.CreateEnumDouble("range", hal::SimDevice::kOutput,
+                                              {"2G", "4G", "8G", "16G"},
+                                              {2.0, 4.0, 8.0, 16.0}, 0);
+    m_simX = m_simDevice.CreateDouble("x", hal::SimDevice::kInput, 0.0);
+    m_simY = m_simDevice.CreateDouble("y", hal::SimDevice::kInput, 0.0);
+    m_simZ = m_simDevice.CreateDouble("z", hal::SimDevice::kInput, 0.0);
   }
 
   m_spi.SetClockRate(3000000);
@@ -58,7 +56,7 @@
     commands[2] = 0;
     m_spi.Transaction(commands, commands, 3);
     if (commands[2] != 0xF2) {
-      DriverStation::ReportError("could not find ADXL362");
+      FRC_ReportError(err::Error, "{}", "could not find ADXL362");
       m_gsPerLSB = 0.0;
       return;
     }
@@ -74,11 +72,17 @@
 
   HAL_Report(HALUsageReporting::kResourceType_ADXL362, port + 1);
 
-  SendableRegistry::GetInstance().AddLW(this, "ADXL362", port);
+  wpi::SendableRegistry::AddLW(this, "ADXL362", port);
+}
+
+SPI::Port ADXL362::GetSpiPort() const {
+  return m_spi.GetPort();
 }
 
 void ADXL362::SetRange(Range range) {
-  if (m_gsPerLSB == 0.0) return;
+  if (m_gsPerLSB == 0.0) {
+    return;
+  }
 
   uint8_t commands[3];
 
@@ -102,21 +106,37 @@
       kFilterCtl_ODR_100Hz | static_cast<uint8_t>((range & 0x03) << 6);
   m_spi.Write(commands, 3);
 
-  if (m_simRange) m_simRange.Set(range);
+  if (m_simRange) {
+    m_simRange.Set(range);
+  }
 }
 
-double ADXL362::GetX() { return GetAcceleration(kAxis_X); }
+double ADXL362::GetX() {
+  return GetAcceleration(kAxis_X);
+}
 
-double ADXL362::GetY() { return GetAcceleration(kAxis_Y); }
+double ADXL362::GetY() {
+  return GetAcceleration(kAxis_Y);
+}
 
-double ADXL362::GetZ() { return GetAcceleration(kAxis_Z); }
+double ADXL362::GetZ() {
+  return GetAcceleration(kAxis_Z);
+}
 
 double ADXL362::GetAcceleration(ADXL362::Axes axis) {
-  if (m_gsPerLSB == 0.0) return 0.0;
+  if (m_gsPerLSB == 0.0) {
+    return 0.0;
+  }
 
-  if (axis == kAxis_X && m_simX) return m_simX.Get();
-  if (axis == kAxis_Y && m_simY) return m_simY.Get();
-  if (axis == kAxis_Z && m_simZ) return m_simZ.Get();
+  if (axis == kAxis_X && m_simX) {
+    return m_simX.Get();
+  }
+  if (axis == kAxis_Y && m_simY) {
+    return m_simY.Get();
+  }
+  if (axis == kAxis_Z && m_simZ) {
+    return m_simZ.Get();
+  }
 
   uint8_t buffer[4];
   uint8_t command[4] = {0, 0, 0, 0};
@@ -162,12 +182,12 @@
   return data;
 }
 
-void ADXL362::InitSendable(SendableBuilder& builder) {
+void ADXL362::InitSendable(nt::NTSendableBuilder& builder) {
   builder.SetSmartDashboardType("3AxisAccelerometer");
   auto x = builder.GetEntry("X").GetHandle();
   auto y = builder.GetEntry("Y").GetHandle();
   auto z = builder.GetEntry("Z").GetHandle();
-  builder.SetUpdateTable([=]() {
+  builder.SetUpdateTable([=] {
     auto data = GetAccelerations();
     nt::NetworkTableEntry(x).SetDouble(data.XAxis);
     nt::NetworkTableEntry(y).SetDouble(data.YAxis);
diff --git a/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp b/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
index 8ea5b41..5a2f625 100644
--- a/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
+++ b/wpilibc/src/main/native/cpp/ADXRS450_Gyro.cpp
@@ -1,22 +1,20 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/ADXRS450_Gyro.h"
 
 #include <hal/FRCUsageReporting.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-#include "frc/DriverStation.h"
+#include "frc/Errors.h"
 #include "frc/Timer.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
-static constexpr auto kSamplePeriod = 0.0005_s;
-static constexpr double kCalibrationSampleTime = 5.0;
+static constexpr auto kSamplePeriod = 0.5_ms;
+static constexpr auto kCalibrationSampleTime = 5_s;
 static constexpr double kDegreePerSecondPerLSB = 0.0125;
 
 // static constexpr int kRateRegister = 0x00;
@@ -32,10 +30,11 @@
 ADXRS450_Gyro::ADXRS450_Gyro() : ADXRS450_Gyro(SPI::kOnboardCS0) {}
 
 ADXRS450_Gyro::ADXRS450_Gyro(SPI::Port port)
-    : m_spi(port), m_port(port), m_simDevice("ADXRS450_Gyro", port) {
+    : m_spi(port), m_port(port), m_simDevice("Gyro:ADXRS450", port) {
   if (m_simDevice) {
-    m_simAngle = m_simDevice.CreateDouble("Angle", false, 0.0);
-    m_simRate = m_simDevice.CreateDouble("Rate", false, 0.0);
+    m_simAngle =
+        m_simDevice.CreateDouble("angle_x", hal::SimDevice::kInput, 0.0);
+    m_simRate = m_simDevice.CreateDouble("rate_x", hal::SimDevice::kInput, 0.0);
   }
 
   m_spi.SetClockRate(3000000);
@@ -47,7 +46,7 @@
   if (!m_simDevice) {
     // Validate the part ID
     if ((ReadRegister(kPIDRegister) & 0xff00) != 0x5200) {
-      DriverStation::ReportError("could not find ADXRS450 gyro");
+      FRC_ReportError(err::Error, "{}", "could not find ADXRS450 gyro");
       return;
     }
 
@@ -59,7 +58,7 @@
 
   HAL_Report(HALUsageReporting::kResourceType_ADXRS450, port + 1);
 
-  SendableRegistry::GetInstance().AddLW(this, "ADXRS450_Gyro", port);
+  wpi::SendableRegistry::AddLW(this, "ADXRS450_Gyro", port);
 }
 
 static bool CalcParity(int v) {
@@ -81,7 +80,9 @@
 
 uint16_t ADXRS450_Gyro::ReadRegister(int reg) {
   int cmd = 0x80000000 | static_cast<int>(reg) << 17;
-  if (!CalcParity(cmd)) cmd |= 1u;
+  if (!CalcParity(cmd)) {
+    cmd |= 1u;
+  }
 
   // big endian
   uint8_t buf[4] = {static_cast<uint8_t>((cmd >> 24) & 0xff),
@@ -91,29 +92,36 @@
 
   m_spi.Write(buf, 4);
   m_spi.Read(false, buf, 4);
-  if ((buf[0] & 0xe0) == 0) return 0;  // error, return 0
+  if ((buf[0] & 0xe0) == 0) {
+    return 0;  // error, return 0
+  }
   return static_cast<uint16_t>((BytesToIntBE(buf) >> 5) & 0xffff);
 }
 
 double ADXRS450_Gyro::GetAngle() const {
-  if (m_simAngle) return m_simAngle.Get();
+  if (m_simAngle) {
+    return m_simAngle.Get();
+  }
   return m_spi.GetAccumulatorIntegratedValue() * kDegreePerSecondPerLSB;
 }
 
 double ADXRS450_Gyro::GetRate() const {
-  if (m_simRate) return m_simRate.Get();
+  if (m_simRate) {
+    return m_simRate.Get();
+  }
   return static_cast<double>(m_spi.GetAccumulatorLastValue()) *
          kDegreePerSecondPerLSB;
 }
 
 void ADXRS450_Gyro::Reset() {
-  if (m_simAngle) m_simAngle.Set(0.0);
-  if (m_simRate) m_simRate.Set(0.0);
+  if (m_simAngle) {
+    m_simAngle.Reset();
+  }
   m_spi.ResetAccumulator();
 }
 
 void ADXRS450_Gyro::Calibrate() {
-  Wait(0.1);
+  Wait(100_ms);
 
   m_spi.SetAccumulatorIntegratedCenter(0);
   m_spi.ResetAccumulator();
@@ -124,4 +132,12 @@
   m_spi.ResetAccumulator();
 }
 
-int ADXRS450_Gyro::GetPort() const { return m_port; }
+int ADXRS450_Gyro::GetPort() const {
+  return m_port;
+}
+
+void ADXRS450_Gyro::InitSendable(wpi::SendableBuilder& builder) {
+  builder.SetSmartDashboardType("Gyro");
+  builder.AddDoubleProperty(
+      "Value", [=] { return GetAngle(); }, nullptr);
+}
diff --git a/wpilibc/src/main/native/cpp/AddressableLED.cpp b/wpilibc/src/main/native/cpp/AddressableLED.cpp
index 81adc7b..759c386 100644
--- a/wpilibc/src/main/native/cpp/AddressableLED.cpp
+++ b/wpilibc/src/main/native/cpp/AddressableLED.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AddressableLED.h"
 
@@ -12,22 +9,25 @@
 #include <hal/HALBase.h>
 #include <hal/PWM.h>
 #include <hal/Ports.h>
+#include <wpi/StackTrace.h>
 
-#include "frc/WPIErrors.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
-AddressableLED::AddressableLED(int port) {
+AddressableLED::AddressableLED(int port) : m_port{port} {
   int32_t status = 0;
 
-  m_pwmHandle = HAL_InitializePWMPort(HAL_GetPort(port), &status);
-  wpi_setHALErrorWithRange(status, 0, HAL_GetNumPWMChannels(), port);
+  auto stack = wpi::GetStackTrace(1);
+  m_pwmHandle =
+      HAL_InitializePWMPort(HAL_GetPort(port), stack.c_str(), &status);
+  FRC_CheckErrorStatus(status, "Port {}", port);
   if (m_pwmHandle == HAL_kInvalidHandle) {
     return;
   }
 
   m_handle = HAL_InitializeAddressableLED(m_pwmHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", port);
   if (m_handle == HAL_kInvalidHandle) {
     HAL_FreePWMPort(m_pwmHandle, &status);
   }
@@ -39,29 +39,30 @@
   HAL_FreeAddressableLED(m_handle);
   int32_t status = 0;
   HAL_FreePWMPort(m_pwmHandle, &status);
+  FRC_ReportError(status, "Port {}", m_port);
 }
 
 void AddressableLED::SetLength(int length) {
   int32_t status = 0;
   HAL_SetAddressableLEDLength(m_handle, length, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {} length {}", m_port, length);
 }
 
 static_assert(sizeof(AddressableLED::LEDData) == sizeof(HAL_AddressableLEDData),
               "LED Structs MUST be the same size");
 
-void AddressableLED::SetData(wpi::ArrayRef<LEDData> ledData) {
+void AddressableLED::SetData(wpi::span<const LEDData> ledData) {
   int32_t status = 0;
   HAL_WriteAddressableLEDData(m_handle, ledData.begin(), ledData.size(),
                               &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void AddressableLED::SetData(std::initializer_list<LEDData> ledData) {
   int32_t status = 0;
   HAL_WriteAddressableLEDData(m_handle, ledData.begin(), ledData.size(),
                               &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void AddressableLED::SetBitTiming(units::nanosecond_t lowTime0,
@@ -72,25 +73,25 @@
   HAL_SetAddressableLEDBitTiming(
       m_handle, lowTime0.to<int32_t>(), highTime0.to<int32_t>(),
       lowTime1.to<int32_t>(), highTime1.to<int32_t>(), &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void AddressableLED::SetSyncTime(units::microsecond_t syncTime) {
   int32_t status = 0;
   HAL_SetAddressableLEDSyncTime(m_handle, syncTime.to<int32_t>(), &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void AddressableLED::Start() {
   int32_t status = 0;
   HAL_StartAddressableLEDOutput(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void AddressableLED::Stop() {
   int32_t status = 0;
   HAL_StopAddressableLEDOutput(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void AddressableLED::LEDData::SetHSV(int h, int s, int v) {
diff --git a/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp b/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp
index 09bc2f5..0b39e64 100644
--- a/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogAccelerometer.cpp
@@ -1,42 +1,37 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AnalogAccelerometer.h"
 
 #include <hal/FRCUsageReporting.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-#include "frc/Base.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
 AnalogAccelerometer::AnalogAccelerometer(int channel)
     : AnalogAccelerometer(std::make_shared<AnalogInput>(channel)) {
-  SendableRegistry::GetInstance().AddChild(this, m_analogInput.get());
+  wpi::SendableRegistry::AddChild(this, m_analogInput.get());
 }
 
 AnalogAccelerometer::AnalogAccelerometer(AnalogInput* channel)
-    : m_analogInput(channel, NullDeleter<AnalogInput>()) {
-  if (channel == nullptr) {
-    wpi_setWPIError(NullParameter);
-  } else {
-    InitAccelerometer();
+    : m_analogInput(channel, wpi::NullDeleter<AnalogInput>()) {
+  if (!channel) {
+    throw FRC_MakeError(err::NullParameter, "{}", "channel");
   }
+  InitAccelerometer();
 }
 
 AnalogAccelerometer::AnalogAccelerometer(std::shared_ptr<AnalogInput> channel)
     : m_analogInput(channel) {
-  if (channel == nullptr) {
-    wpi_setWPIError(NullParameter);
-  } else {
-    InitAccelerometer();
+  if (!channel) {
+    throw FRC_MakeError(err::NullParameter, "{}", "channel");
   }
+  InitAccelerometer();
 }
 
 double AnalogAccelerometer::GetAcceleration() const {
@@ -47,20 +42,20 @@
   m_voltsPerG = sensitivity;
 }
 
-void AnalogAccelerometer::SetZero(double zero) { m_zeroGVoltage = zero; }
+void AnalogAccelerometer::SetZero(double zero) {
+  m_zeroGVoltage = zero;
+}
 
-double AnalogAccelerometer::PIDGet() { return GetAcceleration(); }
-
-void AnalogAccelerometer::InitSendable(SendableBuilder& builder) {
+void AnalogAccelerometer::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Accelerometer");
   builder.AddDoubleProperty(
-      "Value", [=]() { return GetAcceleration(); }, nullptr);
+      "Value", [=] { return GetAcceleration(); }, nullptr);
 }
 
 void AnalogAccelerometer::InitAccelerometer() {
   HAL_Report(HALUsageReporting::kResourceType_Accelerometer,
              m_analogInput->GetChannel() + 1);
 
-  SendableRegistry::GetInstance().AddLW(this, "Accelerometer",
-                                        m_analogInput->GetChannel());
+  wpi::SendableRegistry::AddLW(this, "Accelerometer",
+                               m_analogInput->GetChannel());
 }
diff --git a/wpilibc/src/main/native/cpp/AnalogEncoder.cpp b/wpilibc/src/main/native/cpp/AnalogEncoder.cpp
index c1f4e8d..5f26e87 100644
--- a/wpilibc/src/main/native/cpp/AnalogEncoder.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogEncoder.cpp
@@ -1,29 +1,30 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AnalogEncoder.h"
 
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+
 #include "frc/AnalogInput.h"
-#include "frc/Base.h"
 #include "frc/Counter.h"
-#include "frc/DriverStation.h"
-#include "frc/smartdashboard/SendableBuilder.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
+AnalogEncoder::AnalogEncoder(int channel)
+    : AnalogEncoder(std::make_shared<AnalogInput>(channel)) {}
+
 AnalogEncoder::AnalogEncoder(AnalogInput& analogInput)
-    : m_analogInput{&analogInput, NullDeleter<AnalogInput>{}},
+    : m_analogInput{&analogInput, wpi::NullDeleter<AnalogInput>{}},
       m_analogTrigger{m_analogInput.get()},
       m_counter{} {
   Init();
 }
 
 AnalogEncoder::AnalogEncoder(AnalogInput* analogInput)
-    : m_analogInput{analogInput, NullDeleter<AnalogInput>{}},
+    : m_analogInput{analogInput, wpi::NullDeleter<AnalogInput>{}},
       m_analogTrigger{m_analogInput.get()},
       m_counter{} {
   Init();
@@ -49,12 +50,14 @@
   m_counter.SetDownSource(
       m_analogTrigger.CreateOutput(AnalogTriggerType::kFallingPulse));
 
-  SendableRegistry::GetInstance().AddLW(this, "DutyCycle Encoder",
-                                        m_analogInput->GetChannel());
+  wpi::SendableRegistry::AddLW(this, "DutyCycle Encoder",
+                               m_analogInput->GetChannel());
 }
 
 units::turn_t AnalogEncoder::Get() const {
-  if (m_simPosition) return units::turn_t{m_simPosition.Get()};
+  if (m_simPosition) {
+    return units::turn_t{m_simPosition.Get()};
+  }
 
   // As the values are not atomic, keep trying until we get 2 reads of the same
   // value If we don't within 10 attempts, error
@@ -70,13 +73,16 @@
     }
   }
 
-  frc::DriverStation::GetInstance().ReportWarning(
+  FRC_ReportError(
+      warn::Warning, "{}",
       "Failed to read Analog Encoder. Potential Speed Overrun. Returning last "
       "value");
   return m_lastPosition;
 }
 
-double AnalogEncoder::GetPositionOffset() const { return m_positionOffset; }
+double AnalogEncoder::GetPositionOffset() const {
+  return m_positionOffset;
+}
 
 void AnalogEncoder::SetDistancePerRotation(double distancePerRotation) {
   m_distancePerRotation = distancePerRotation;
@@ -87,7 +93,7 @@
 }
 
 double AnalogEncoder::GetDistance() const {
-  return Get().to<double>() * GetDistancePerRotation();
+  return Get().value() * GetDistancePerRotation();
 }
 
 void AnalogEncoder::Reset() {
@@ -95,9 +101,11 @@
   m_positionOffset = m_analogInput->GetVoltage();
 }
 
-int AnalogEncoder::GetChannel() const { return m_analogInput->GetChannel(); }
+int AnalogEncoder::GetChannel() const {
+  return m_analogInput->GetChannel();
+}
 
-void AnalogEncoder::InitSendable(SendableBuilder& builder) {
+void AnalogEncoder::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("AbsoluteEncoder");
   builder.AddDoubleProperty(
       "Distance", [this] { return this->GetDistance(); }, nullptr);
diff --git a/wpilibc/src/main/native/cpp/AnalogGyro.cpp b/wpilibc/src/main/native/cpp/AnalogGyro.cpp
index 2ec4439..106293b 100644
--- a/wpilibc/src/main/native/cpp/AnalogGyro.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogGyro.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AnalogGyro.h"
 
@@ -13,89 +10,83 @@
 #include <hal/AnalogGyro.h>
 #include <hal/Errors.h>
 #include <hal/FRCUsageReporting.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/StackTrace.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 #include "frc/AnalogInput.h"
-#include "frc/Base.h"
+#include "frc/Errors.h"
 #include "frc/Timer.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
 AnalogGyro::AnalogGyro(int channel)
     : AnalogGyro(std::make_shared<AnalogInput>(channel)) {
-  SendableRegistry::GetInstance().AddChild(this, m_analog.get());
+  wpi::SendableRegistry::AddChild(this, m_analog.get());
 }
 
 AnalogGyro::AnalogGyro(AnalogInput* channel)
-    : AnalogGyro(
-          std::shared_ptr<AnalogInput>(channel, NullDeleter<AnalogInput>())) {}
+    : AnalogGyro(std::shared_ptr<AnalogInput>(
+          channel, wpi::NullDeleter<AnalogInput>())) {}
 
 AnalogGyro::AnalogGyro(std::shared_ptr<AnalogInput> channel)
     : m_analog(channel) {
-  if (channel == nullptr) {
-    wpi_setWPIError(NullParameter);
-  } else {
-    InitGyro();
-    Calibrate();
+  if (!channel) {
+    throw FRC_MakeError(err::NullParameter, "{}", "channel");
   }
+  InitGyro();
+  Calibrate();
 }
 
 AnalogGyro::AnalogGyro(int channel, int center, double offset)
     : AnalogGyro(std::make_shared<AnalogInput>(channel), center, offset) {
-  SendableRegistry::GetInstance().AddChild(this, m_analog.get());
+  wpi::SendableRegistry::AddChild(this, m_analog.get());
 }
 
 AnalogGyro::AnalogGyro(std::shared_ptr<AnalogInput> channel, int center,
                        double offset)
     : m_analog(channel) {
-  if (channel == nullptr) {
-    wpi_setWPIError(NullParameter);
-  } else {
-    InitGyro();
-    int32_t status = 0;
-    HAL_SetAnalogGyroParameters(m_gyroHandle, kDefaultVoltsPerDegreePerSecond,
-                                offset, center, &status);
-    if (status != 0) {
-      wpi_setHALError(status);
-      m_gyroHandle = HAL_kInvalidHandle;
-      return;
-    }
-    Reset();
+  if (!channel) {
+    throw FRC_MakeError(err::NullParameter, "{}", "channel");
   }
+  InitGyro();
+  int32_t status = 0;
+  HAL_SetAnalogGyroParameters(m_gyroHandle, kDefaultVoltsPerDegreePerSecond,
+                              offset, center, &status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
+  Reset();
 }
 
-AnalogGyro::~AnalogGyro() { HAL_FreeAnalogGyro(m_gyroHandle); }
+AnalogGyro::~AnalogGyro() {
+  HAL_FreeAnalogGyro(m_gyroHandle);
+}
 
 double AnalogGyro::GetAngle() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double value = HAL_GetAnalogGyroAngle(m_gyroHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
   return value;
 }
 
 double AnalogGyro::GetRate() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double value = HAL_GetAnalogGyroRate(m_gyroHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
   return value;
 }
 
 int AnalogGyro::GetCenter() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int value = HAL_GetAnalogGyroCenter(m_gyroHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
   return value;
 }
 
 double AnalogGyro::GetOffset() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double value = HAL_GetAnalogGyroOffset(m_gyroHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
   return value;
 }
 
@@ -103,65 +94,51 @@
   int32_t status = 0;
   HAL_SetAnalogGyroVoltsPerDegreePerSecond(m_gyroHandle,
                                            voltsPerDegreePerSecond, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
 }
 
 void AnalogGyro::SetDeadband(double volts) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAnalogGyroDeadband(m_gyroHandle, volts, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
 }
 
 void AnalogGyro::Reset() {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_ResetAnalogGyro(m_gyroHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
 }
 
 void AnalogGyro::InitGyro() {
-  if (StatusIsFatal()) return;
   if (m_gyroHandle == HAL_kInvalidHandle) {
     int32_t status = 0;
-    m_gyroHandle = HAL_InitializeAnalogGyro(m_analog->m_port, &status);
-    if (status == PARAMETER_OUT_OF_RANGE) {
-      wpi_setWPIErrorWithContext(ParameterOutOfRange,
-                                 " channel (must be accumulator channel)");
-      m_analog = nullptr;
-      m_gyroHandle = HAL_kInvalidHandle;
-      return;
-    }
-    if (status != 0) {
-      wpi_setHALError(status);
-      m_analog = nullptr;
-      m_gyroHandle = HAL_kInvalidHandle;
-      return;
-    }
+    std::string stackTrace = wpi::GetStackTrace(1);
+    m_gyroHandle =
+        HAL_InitializeAnalogGyro(m_analog->m_port, stackTrace.c_str(), &status);
+    FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
   }
 
   int32_t status = 0;
   HAL_SetupAnalogGyro(m_gyroHandle, &status);
-  if (status != 0) {
-    wpi_setHALError(status);
-    m_analog = nullptr;
-    m_gyroHandle = HAL_kInvalidHandle;
-    return;
-  }
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
 
   HAL_Report(HALUsageReporting::kResourceType_Gyro, m_analog->GetChannel() + 1);
 
-  SendableRegistry::GetInstance().AddLW(this, "AnalogGyro",
-                                        m_analog->GetChannel());
+  wpi::SendableRegistry::AddLW(this, "AnalogGyro", m_analog->GetChannel());
 }
 
 void AnalogGyro::Calibrate() {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_CalibrateAnalogGyro(m_gyroHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_analog->GetChannel());
 }
 
 std::shared_ptr<AnalogInput> AnalogGyro::GetAnalogInput() const {
   return m_analog;
 }
+
+void AnalogGyro::InitSendable(wpi::SendableBuilder& builder) {
+  builder.SetSmartDashboardType("Gyro");
+  builder.AddDoubleProperty(
+      "Value", [=] { return GetAngle(); }, nullptr);
+}
diff --git a/wpilibc/src/main/native/cpp/AnalogInput.cpp b/wpilibc/src/main/native/cpp/AnalogInput.cpp
index 11c30ee..dfa5764 100644
--- a/wpilibc/src/main/native/cpp/AnalogInput.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogInput.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AnalogInput.h"
 
@@ -12,222 +9,193 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <hal/Ports.h>
+#include <wpi/StackTrace.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
 #include "frc/Timer.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
 AnalogInput::AnalogInput(int channel) {
   if (!SensorUtil::CheckAnalogInputChannel(channel)) {
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange,
-                               "Analog Input " + wpi::Twine(channel));
-    return;
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}", channel);
   }
 
   m_channel = channel;
 
   HAL_PortHandle port = HAL_GetPort(channel);
   int32_t status = 0;
-  m_port = HAL_InitializeAnalogInputPort(port, &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumAnalogInputs(), channel);
-    m_channel = std::numeric_limits<int>::max();
-    m_port = HAL_kInvalidHandle;
-    return;
-  }
+  std::string stackTrace = wpi::GetStackTrace(1);
+  m_port = HAL_InitializeAnalogInputPort(port, stackTrace.c_str(), &status);
+  FRC_CheckErrorStatus(status, "Channel {}", channel);
 
   HAL_Report(HALUsageReporting::kResourceType_AnalogChannel, channel + 1);
 
-  SendableRegistry::GetInstance().AddLW(this, "AnalogInput", channel);
+  wpi::SendableRegistry::AddLW(this, "AnalogInput", channel);
 }
 
-AnalogInput::~AnalogInput() { HAL_FreeAnalogInputPort(m_port); }
+AnalogInput::~AnalogInput() {
+  HAL_FreeAnalogInputPort(m_port);
+}
 
 int AnalogInput::GetValue() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int value = HAL_GetAnalogValue(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return value;
 }
 
 int AnalogInput::GetAverageValue() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int value = HAL_GetAnalogAverageValue(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return value;
 }
 
 double AnalogInput::GetVoltage() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double voltage = HAL_GetAnalogVoltage(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return voltage;
 }
 
 double AnalogInput::GetAverageVoltage() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double voltage = HAL_GetAnalogAverageVoltage(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return voltage;
 }
 
 int AnalogInput::GetChannel() const {
-  if (StatusIsFatal()) return 0;
   return m_channel;
 }
 
 void AnalogInput::SetAverageBits(int bits) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAnalogAverageBits(m_port, bits, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 int AnalogInput::GetAverageBits() const {
   int32_t status = 0;
   int averageBits = HAL_GetAnalogAverageBits(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return averageBits;
 }
 
 void AnalogInput::SetOversampleBits(int bits) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAnalogOversampleBits(m_port, bits, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 int AnalogInput::GetOversampleBits() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int oversampleBits = HAL_GetAnalogOversampleBits(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return oversampleBits;
 }
 
 int AnalogInput::GetLSBWeight() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int lsbWeight = HAL_GetAnalogLSBWeight(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return lsbWeight;
 }
 
 int AnalogInput::GetOffset() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int offset = HAL_GetAnalogOffset(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return offset;
 }
 
 bool AnalogInput::IsAccumulatorChannel() const {
-  if (StatusIsFatal()) return false;
   int32_t status = 0;
   bool isAccum = HAL_IsAccumulatorChannel(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return isAccum;
 }
 
 void AnalogInput::InitAccumulator() {
-  if (StatusIsFatal()) return;
   m_accumulatorOffset = 0;
   int32_t status = 0;
   HAL_InitAccumulator(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void AnalogInput::SetAccumulatorInitialValue(int64_t initialValue) {
-  if (StatusIsFatal()) return;
   m_accumulatorOffset = initialValue;
 }
 
 void AnalogInput::ResetAccumulator() {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_ResetAccumulator(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 
-  if (!StatusIsFatal()) {
-    // Wait until the next sample, so the next call to GetAccumulator*()
-    // won't have old values.
-    const double sampleTime = 1.0 / GetSampleRate();
-    const double overSamples = 1 << GetOversampleBits();
-    const double averageSamples = 1 << GetAverageBits();
-    Wait(sampleTime * overSamples * averageSamples);
-  }
+  // Wait until the next sample, so the next call to GetAccumulator*()
+  // won't have old values.
+  const double sampleTime = 1.0 / GetSampleRate();
+  const double overSamples = 1 << GetOversampleBits();
+  const double averageSamples = 1 << GetAverageBits();
+  Wait(units::second_t{sampleTime * overSamples * averageSamples});
 }
 
 void AnalogInput::SetAccumulatorCenter(int center) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAccumulatorCenter(m_port, center, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void AnalogInput::SetAccumulatorDeadband(int deadband) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAccumulatorDeadband(m_port, deadband, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 int64_t AnalogInput::GetAccumulatorValue() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int64_t value = HAL_GetAccumulatorValue(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return value + m_accumulatorOffset;
 }
 
 int64_t AnalogInput::GetAccumulatorCount() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int64_t count = HAL_GetAccumulatorCount(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return count;
 }
 
 void AnalogInput::GetAccumulatorOutput(int64_t& value, int64_t& count) const {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_GetAccumulatorOutput(m_port, &value, &count, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   value += m_accumulatorOffset;
 }
 
 void AnalogInput::SetSampleRate(double samplesPerSecond) {
   int32_t status = 0;
   HAL_SetAnalogSampleRate(samplesPerSecond, &status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetSampleRate");
 }
 
 double AnalogInput::GetSampleRate() {
   int32_t status = 0;
   double sampleRate = HAL_GetAnalogSampleRate(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetSampleRate");
   return sampleRate;
 }
 
-double AnalogInput::PIDGet() {
-  if (StatusIsFatal()) return 0.0;
-  return GetAverageVoltage();
-}
-
 void AnalogInput::SetSimDevice(HAL_SimDeviceHandle device) {
   HAL_SetAnalogInputSimDevice(m_port, device);
 }
 
-void AnalogInput::InitSendable(SendableBuilder& builder) {
+void AnalogInput::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Analog Input");
   builder.AddDoubleProperty(
-      "Value", [=]() { return GetAverageVoltage(); }, nullptr);
+      "Value", [=] { return GetAverageVoltage(); }, nullptr);
 }
diff --git a/wpilibc/src/main/native/cpp/AnalogOutput.cpp b/wpilibc/src/main/native/cpp/AnalogOutput.cpp
index bd8126a..a391271 100644
--- a/wpilibc/src/main/native/cpp/AnalogOutput.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogOutput.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2014-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AnalogOutput.h"
 
@@ -14,62 +11,56 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <hal/Ports.h>
+#include <wpi/StackTrace.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
 AnalogOutput::AnalogOutput(int channel) {
   if (!SensorUtil::CheckAnalogOutputChannel(channel)) {
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange,
-                               "analog output " + wpi::Twine(channel));
-    m_channel = std::numeric_limits<int>::max();
-    m_port = HAL_kInvalidHandle;
-    return;
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}", channel);
   }
 
   m_channel = channel;
 
   HAL_PortHandle port = HAL_GetPort(m_channel);
   int32_t status = 0;
-  m_port = HAL_InitializeAnalogOutputPort(port, &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumAnalogOutputs(), channel);
-    m_channel = std::numeric_limits<int>::max();
-    m_port = HAL_kInvalidHandle;
-    return;
-  }
+  std::string stackTrace = wpi::GetStackTrace(1);
+  m_port = HAL_InitializeAnalogOutputPort(port, stackTrace.c_str(), &status);
+  FRC_CheckErrorStatus(status, "Channel {}", channel);
 
   HAL_Report(HALUsageReporting::kResourceType_AnalogOutput, m_channel + 1);
-  SendableRegistry::GetInstance().AddLW(this, "AnalogOutput", m_channel);
+  wpi::SendableRegistry::AddLW(this, "AnalogOutput", m_channel);
 }
 
-AnalogOutput::~AnalogOutput() { HAL_FreeAnalogOutputPort(m_port); }
+AnalogOutput::~AnalogOutput() {
+  HAL_FreeAnalogOutputPort(m_port);
+}
 
 void AnalogOutput::SetVoltage(double voltage) {
   int32_t status = 0;
   HAL_SetAnalogOutput(m_port, voltage, &status);
-
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 double AnalogOutput::GetVoltage() const {
   int32_t status = 0;
   double voltage = HAL_GetAnalogOutput(m_port, &status);
-
-  wpi_setHALError(status);
-
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return voltage;
 }
 
-int AnalogOutput::GetChannel() const { return m_channel; }
+int AnalogOutput::GetChannel() const {
+  return m_channel;
+}
 
-void AnalogOutput::InitSendable(SendableBuilder& builder) {
+void AnalogOutput::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Analog Output");
   builder.AddDoubleProperty(
-      "Value", [=]() { return GetVoltage(); },
+      "Value", [=] { return GetVoltage(); },
       [=](double value) { SetVoltage(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/AnalogPotentiometer.cpp b/wpilibc/src/main/native/cpp/AnalogPotentiometer.cpp
index c7aedd6..ba94613 100644
--- a/wpilibc/src/main/native/cpp/AnalogPotentiometer.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogPotentiometer.cpp
@@ -1,16 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AnalogPotentiometer.h"
 
-#include "frc/Base.h"
+#include <utility>
+
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
+
 #include "frc/RobotController.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
@@ -18,20 +18,22 @@
                                          double offset)
     : AnalogPotentiometer(std::make_shared<AnalogInput>(channel), fullRange,
                           offset) {
-  SendableRegistry::GetInstance().AddChild(this, m_analog_input.get());
+  wpi::SendableRegistry::AddChild(this, m_analog_input.get());
 }
 
 AnalogPotentiometer::AnalogPotentiometer(AnalogInput* input, double fullRange,
                                          double offset)
     : AnalogPotentiometer(
-          std::shared_ptr<AnalogInput>(input, NullDeleter<AnalogInput>()),
+          std::shared_ptr<AnalogInput>(input, wpi::NullDeleter<AnalogInput>()),
           fullRange, offset) {}
 
 AnalogPotentiometer::AnalogPotentiometer(std::shared_ptr<AnalogInput> input,
                                          double fullRange, double offset)
-    : m_analog_input(input), m_fullRange(fullRange), m_offset(offset) {
-  SendableRegistry::GetInstance().AddLW(this, "AnalogPotentiometer",
-                                        m_analog_input->GetChannel());
+    : m_analog_input(std::move(input)),
+      m_fullRange(fullRange),
+      m_offset(offset) {
+  wpi::SendableRegistry::AddLW(this, "AnalogPotentiometer",
+                               m_analog_input->GetChannel());
 }
 
 double AnalogPotentiometer::Get() const {
@@ -41,10 +43,8 @@
          m_offset;
 }
 
-double AnalogPotentiometer::PIDGet() { return Get(); }
-
-void AnalogPotentiometer::InitSendable(SendableBuilder& builder) {
+void AnalogPotentiometer::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Analog Input");
   builder.AddDoubleProperty(
-      "Value", [=]() { return Get(); }, nullptr);
+      "Value", [=] { return Get(); }, nullptr);
 }
diff --git a/wpilibc/src/main/native/cpp/AnalogTrigger.cpp b/wpilibc/src/main/native/cpp/AnalogTrigger.cpp
index 45821ba..ea33c73 100644
--- a/wpilibc/src/main/native/cpp/AnalogTrigger.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogTrigger.cpp
@@ -1,156 +1,129 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AnalogTrigger.h"
 
 #include <utility>
 
+#include <hal/AnalogTrigger.h>
 #include <hal/FRCUsageReporting.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 #include "frc/AnalogInput.h"
-#include "frc/Base.h"
 #include "frc/DutyCycle.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
 AnalogTrigger::AnalogTrigger(int channel)
     : AnalogTrigger(new AnalogInput(channel)) {
   m_ownsAnalog = true;
-  SendableRegistry::GetInstance().AddChild(this, m_analogInput);
+  wpi::SendableRegistry::AddChild(this, m_analogInput);
 }
 
 AnalogTrigger::AnalogTrigger(AnalogInput* input) {
   m_analogInput = input;
   int32_t status = 0;
   m_trigger = HAL_InitializeAnalogTrigger(input->m_port, &status);
-  if (status != 0) {
-    wpi_setHALError(status);
-    m_trigger = HAL_kInvalidHandle;
-    return;
-  }
+  FRC_CheckErrorStatus(status, "Channel {}", input->GetChannel());
   int index = GetIndex();
 
   HAL_Report(HALUsageReporting::kResourceType_AnalogTrigger, index + 1);
-  SendableRegistry::GetInstance().AddLW(this, "AnalogTrigger", index);
+  wpi::SendableRegistry::AddLW(this, "AnalogTrigger", index);
 }
 
 AnalogTrigger::AnalogTrigger(DutyCycle* input) {
   m_dutyCycle = input;
   int32_t status = 0;
   m_trigger = HAL_InitializeAnalogTriggerDutyCycle(input->m_handle, &status);
-  if (status != 0) {
-    wpi_setHALError(status);
-    m_trigger = HAL_kInvalidHandle;
-    return;
-  }
+  FRC_CheckErrorStatus(status, "Channel {}", m_dutyCycle->GetSourceChannel());
   int index = GetIndex();
 
   HAL_Report(HALUsageReporting::kResourceType_AnalogTrigger, index + 1);
-  SendableRegistry::GetInstance().AddLW(this, "AnalogTrigger", index);
+  wpi::SendableRegistry::AddLW(this, "AnalogTrigger", index);
 }
 
 AnalogTrigger::~AnalogTrigger() {
   int32_t status = 0;
   HAL_CleanAnalogTrigger(m_trigger, &status);
+  FRC_ReportError(status, "Channel {}", GetSourceChannel());
 
   if (m_ownsAnalog) {
     delete m_analogInput;
   }
 }
 
-AnalogTrigger::AnalogTrigger(AnalogTrigger&& rhs)
-    : ErrorBase(std::move(rhs)),
-      SendableHelper(std::move(rhs)),
-      m_trigger(std::move(rhs.m_trigger)) {
-  std::swap(m_analogInput, rhs.m_analogInput);
-  std::swap(m_dutyCycle, rhs.m_dutyCycle);
-  std::swap(m_ownsAnalog, rhs.m_ownsAnalog);
-}
-
-AnalogTrigger& AnalogTrigger::operator=(AnalogTrigger&& rhs) {
-  ErrorBase::operator=(std::move(rhs));
-  SendableHelper::operator=(std::move(rhs));
-
-  m_trigger = std::move(rhs.m_trigger);
-  std::swap(m_analogInput, rhs.m_analogInput);
-  std::swap(m_dutyCycle, rhs.m_dutyCycle);
-  std::swap(m_ownsAnalog, rhs.m_ownsAnalog);
-
-  return *this;
-}
-
 void AnalogTrigger::SetLimitsVoltage(double lower, double upper) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAnalogTriggerLimitsVoltage(m_trigger, lower, upper, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
 }
 
 void AnalogTrigger::SetLimitsDutyCycle(double lower, double upper) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAnalogTriggerLimitsDutyCycle(m_trigger, lower, upper, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
 }
 
 void AnalogTrigger::SetLimitsRaw(int lower, int upper) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAnalogTriggerLimitsRaw(m_trigger, lower, upper, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
 }
 
 void AnalogTrigger::SetAveraged(bool useAveragedValue) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAnalogTriggerAveraged(m_trigger, useAveragedValue, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
 }
 
 void AnalogTrigger::SetFiltered(bool useFilteredValue) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetAnalogTriggerFiltered(m_trigger, useFilteredValue, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
 }
 
 int AnalogTrigger::GetIndex() const {
-  if (StatusIsFatal()) return -1;
   int32_t status = 0;
   auto ret = HAL_GetAnalogTriggerFPGAIndex(m_trigger, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   return ret;
 }
 
 bool AnalogTrigger::GetInWindow() {
-  if (StatusIsFatal()) return false;
   int32_t status = 0;
   bool result = HAL_GetAnalogTriggerInWindow(m_trigger, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   return result;
 }
 
 bool AnalogTrigger::GetTriggerState() {
-  if (StatusIsFatal()) return false;
   int32_t status = 0;
   bool result = HAL_GetAnalogTriggerTriggerState(m_trigger, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   return result;
 }
 
 std::shared_ptr<AnalogTriggerOutput> AnalogTrigger::CreateOutput(
     AnalogTriggerType type) const {
-  if (StatusIsFatal()) return nullptr;
   return std::shared_ptr<AnalogTriggerOutput>(
-      new AnalogTriggerOutput(*this, type), NullDeleter<AnalogTriggerOutput>());
+      new AnalogTriggerOutput(*this, type));
 }
 
-void AnalogTrigger::InitSendable(SendableBuilder& builder) {
-  if (m_ownsAnalog) m_analogInput->InitSendable(builder);
+void AnalogTrigger::InitSendable(wpi::SendableBuilder& builder) {
+  if (m_ownsAnalog) {
+    m_analogInput->InitSendable(builder);
+  }
+}
+
+int AnalogTrigger::GetSourceChannel() const {
+  if (m_analogInput) {
+    return m_analogInput->GetChannel();
+  } else if (m_dutyCycle) {
+    return m_dutyCycle->GetSourceChannel();
+  } else {
+    return -1;
+  }
 }
diff --git a/wpilibc/src/main/native/cpp/AnalogTriggerOutput.cpp b/wpilibc/src/main/native/cpp/AnalogTriggerOutput.cpp
index 8fba479..93d1969 100644
--- a/wpilibc/src/main/native/cpp/AnalogTriggerOutput.cpp
+++ b/wpilibc/src/main/native/cpp/AnalogTriggerOutput.cpp
@@ -1,16 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/AnalogTriggerOutput.h"
 
+#include <hal/AnalogTrigger.h>
 #include <hal/FRCUsageReporting.h>
 
 #include "frc/AnalogTrigger.h"
-#include "frc/WPIErrors.h"
+#include "frc/AnalogTriggerType.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
@@ -19,7 +18,7 @@
   bool result = HAL_GetAnalogTriggerOutput(
       m_trigger->m_trigger, static_cast<HAL_AnalogTriggerType>(m_outputType),
       &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Get");
   return result;
 }
 
@@ -31,11 +30,15 @@
   return m_outputType;
 }
 
-bool AnalogTriggerOutput::IsAnalogTrigger() const { return true; }
+bool AnalogTriggerOutput::IsAnalogTrigger() const {
+  return true;
+}
 
-int AnalogTriggerOutput::GetChannel() const { return m_trigger->GetIndex(); }
+int AnalogTriggerOutput::GetChannel() const {
+  return m_trigger->GetIndex();
+}
 
-void AnalogTriggerOutput::InitSendable(SendableBuilder&) {}
+void AnalogTriggerOutput::InitSendable(wpi::SendableBuilder&) {}
 
 AnalogTriggerOutput::AnalogTriggerOutput(const AnalogTrigger& trigger,
                                          AnalogTriggerType outputType)
diff --git a/wpilibc/src/main/native/cpp/AsynchronousInterrupt.cpp b/wpilibc/src/main/native/cpp/AsynchronousInterrupt.cpp
new file mode 100644
index 0000000..d45d5e4
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/AsynchronousInterrupt.cpp
@@ -0,0 +1,67 @@
+// 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.
+
+#include "frc/AsynchronousInterrupt.h"
+
+#include <frc/DigitalSource.h>
+
+using namespace frc;
+
+AsynchronousInterrupt::AsynchronousInterrupt(
+    DigitalSource& source, std::function<void(bool, bool)> callback)
+    : m_interrupt{source}, m_callback{std::move(callback)} {}
+AsynchronousInterrupt::AsynchronousInterrupt(
+    DigitalSource* source, std::function<void(bool, bool)> callback)
+    : m_interrupt{source}, m_callback{std::move(callback)} {}
+AsynchronousInterrupt::AsynchronousInterrupt(
+    std::shared_ptr<DigitalSource> source,
+    std::function<void(bool, bool)> callback)
+    : m_interrupt{source}, m_callback{std::move(callback)} {}
+
+AsynchronousInterrupt::~AsynchronousInterrupt() {
+  Disable();
+}
+
+void AsynchronousInterrupt::ThreadMain() {
+  while (m_keepRunning) {
+    auto result = m_interrupt.WaitForInterrupt(10_s, false);
+    if (!m_keepRunning) {
+      break;
+    }
+    if (result == SynchronousInterrupt::WaitResult::kTimeout) {
+      continue;
+    }
+    m_callback((result & SynchronousInterrupt::WaitResult::kRisingEdge) != 0,
+               (result & SynchronousInterrupt::WaitResult::kFallingEdge) != 0);
+  }
+}
+
+void AsynchronousInterrupt::Enable() {
+  if (m_keepRunning) {
+    return;
+  }
+
+  m_keepRunning = true;
+  m_thread = std::thread([this] { this->ThreadMain(); });
+}
+
+void AsynchronousInterrupt::Disable() {
+  m_keepRunning = false;
+  m_interrupt.WakeupWaitingInterrupt();
+  if (m_thread.joinable()) {
+    m_thread.join();
+  }
+}
+
+void AsynchronousInterrupt::SetInterruptEdges(bool risingEdge,
+                                              bool fallingEdge) {
+  m_interrupt.SetInterruptEdges(risingEdge, fallingEdge);
+}
+
+units::second_t AsynchronousInterrupt::GetRisingTimestamp() {
+  return m_interrupt.GetRisingTimestamp();
+}
+units::second_t AsynchronousInterrupt::GetFallingTimestamp() {
+  return m_interrupt.GetFallingTimestamp();
+}
diff --git a/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp b/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
index efc0fe6..1b8da85 100644
--- a/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
+++ b/wpilibc/src/main/native/cpp/BuiltInAccelerometer.cpp
@@ -1,18 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2014-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/BuiltInAccelerometer.h"
 
 #include <hal/Accelerometer.h>
 #include <hal/FRCUsageReporting.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
@@ -21,32 +18,38 @@
 
   HAL_Report(HALUsageReporting::kResourceType_Accelerometer, 0, 0,
              "Built-in accelerometer");
-  SendableRegistry::GetInstance().AddLW(this, "BuiltInAccel");
+  wpi::SendableRegistry::AddLW(this, "BuiltInAccel");
 }
 
 void BuiltInAccelerometer::SetRange(Range range) {
   if (range == kRange_16G) {
-    wpi_setWPIErrorWithContext(
-        ParameterOutOfRange, "16G range not supported (use k2G, k4G, or k8G)");
+    throw FRC_MakeError(err::ParameterOutOfRange, "{}",
+                        "16G range not supported (use k2G, k4G, or k8G)");
   }
 
   HAL_SetAccelerometerActive(false);
-  HAL_SetAccelerometerRange((HAL_AccelerometerRange)range);
+  HAL_SetAccelerometerRange(static_cast<HAL_AccelerometerRange>(range));
   HAL_SetAccelerometerActive(true);
 }
 
-double BuiltInAccelerometer::GetX() { return HAL_GetAccelerometerX(); }
+double BuiltInAccelerometer::GetX() {
+  return HAL_GetAccelerometerX();
+}
 
-double BuiltInAccelerometer::GetY() { return HAL_GetAccelerometerY(); }
+double BuiltInAccelerometer::GetY() {
+  return HAL_GetAccelerometerY();
+}
 
-double BuiltInAccelerometer::GetZ() { return HAL_GetAccelerometerZ(); }
+double BuiltInAccelerometer::GetZ() {
+  return HAL_GetAccelerometerZ();
+}
 
-void BuiltInAccelerometer::InitSendable(SendableBuilder& builder) {
+void BuiltInAccelerometer::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("3AxisAccelerometer");
   builder.AddDoubleProperty(
-      "X", [=]() { return GetX(); }, nullptr);
+      "X", [=] { return GetX(); }, nullptr);
   builder.AddDoubleProperty(
-      "Y", [=]() { return GetY(); }, nullptr);
+      "Y", [=] { return GetY(); }, nullptr);
   builder.AddDoubleProperty(
-      "Z", [=]() { return GetZ(); }, nullptr);
+      "Z", [=] { return GetZ(); }, nullptr);
 }
diff --git a/wpilibc/src/main/native/cpp/CAN.cpp b/wpilibc/src/main/native/cpp/CAN.cpp
index e56e435..e26ca37 100644
--- a/wpilibc/src/main/native/cpp/CAN.cpp
+++ b/wpilibc/src/main/native/cpp/CAN.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/CAN.h"
 
@@ -14,17 +11,15 @@
 #include <hal/Errors.h>
 #include <hal/FRCUsageReporting.h>
 
+#include "frc/Errors.h"
+
 using namespace frc;
 
 CAN::CAN(int deviceId) {
   int32_t status = 0;
   m_handle =
       HAL_InitializeCAN(kTeamManufacturer, deviceId, kTeamDeviceType, &status);
-  if (status != 0) {
-    wpi_setHALError(status);
-    m_handle = HAL_kInvalidHandle;
-    return;
-  }
+  FRC_CheckErrorStatus(status, "device id {}", deviceId);
 
   HAL_Report(HALUsageReporting::kResourceType_CAN, deviceId + 1);
 }
@@ -34,17 +29,13 @@
   m_handle = HAL_InitializeCAN(
       static_cast<HAL_CANManufacturer>(deviceManufacturer), deviceId,
       static_cast<HAL_CANDeviceType>(deviceType), &status);
-  if (status != 0) {
-    wpi_setHALError(status);
-    m_handle = HAL_kInvalidHandle;
-    return;
-  }
+  FRC_CheckErrorStatus(status, "device id {} mfg {} type {}", deviceId,
+                       deviceManufacturer, deviceType);
 
   HAL_Report(HALUsageReporting::kResourceType_CAN, deviceId + 1);
 }
 
 CAN::~CAN() {
-  if (StatusIsFatal()) return;
   if (m_handle != HAL_kInvalidHandle) {
     HAL_CleanCAN(m_handle);
     m_handle = HAL_kInvalidHandle;
@@ -54,20 +45,20 @@
 void CAN::WritePacket(const uint8_t* data, int length, int apiId) {
   int32_t status = 0;
   HAL_WriteCANPacket(m_handle, data, length, apiId, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "WritePacket");
 }
 
 void CAN::WritePacketRepeating(const uint8_t* data, int length, int apiId,
                                int repeatMs) {
   int32_t status = 0;
   HAL_WriteCANPacketRepeating(m_handle, data, length, apiId, repeatMs, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "WritePacketRepeating");
 }
 
 void CAN::WriteRTRFrame(int length, int apiId) {
   int32_t status = 0;
   HAL_WriteCANRTRFrame(m_handle, length, apiId, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "WriteRTRFrame");
 }
 
 int CAN::WritePacketNoError(const uint8_t* data, int length, int apiId) {
@@ -92,7 +83,7 @@
 void CAN::StopPacketRepeating(int apiId) {
   int32_t status = 0;
   HAL_StopCANPacketRepeating(m_handle, apiId, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "StopPacketRepeating");
 }
 
 bool CAN::ReadPacketNew(int apiId, CANData* data) {
@@ -103,7 +94,7 @@
     return false;
   }
   if (status != 0) {
-    wpi_setHALError(status);
+    FRC_CheckErrorStatus(status, "{}", "ReadPacketNew");
     return false;
   } else {
     return true;
@@ -118,7 +109,7 @@
     return false;
   }
   if (status != 0) {
-    wpi_setHALError(status);
+    FRC_CheckErrorStatus(status, "{}", "ReadPacketLatest");
     return false;
   } else {
     return true;
@@ -134,7 +125,7 @@
     return false;
   }
   if (status != 0) {
-    wpi_setHALError(status);
+    FRC_CheckErrorStatus(status, "{}", "ReadPacketTimeout");
     return false;
   } else {
     return true;
diff --git a/wpilibc/src/main/native/cpp/Compressor.cpp b/wpilibc/src/main/native/cpp/Compressor.cpp
index e40758f..0302acb 100644
--- a/wpilibc/src/main/native/cpp/Compressor.cpp
+++ b/wpilibc/src/main/native/cpp/Compressor.cpp
@@ -1,221 +1,72 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2014-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Compressor.h"
 
-#include <hal/Compressor.h>
 #include <hal/FRCUsageReporting.h>
 #include <hal/Ports.h>
-#include <hal/Solenoid.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
-Compressor::Compressor(int pcmID) : m_module(pcmID) {
-  int32_t status = 0;
-  m_compressorHandle = HAL_InitializeCompressor(m_module, &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumPCMModules(), pcmID);
-    return;
+Compressor::Compressor(int module, PneumaticsModuleType moduleType)
+    : m_module{PneumaticsBase::GetForType(module, moduleType)} {
+  if (!m_module->ReserveCompressor()) {
+    throw FRC_MakeError(err::ResourceAlreadyAllocated, "{}", module);
   }
+
   SetClosedLoopControl(true);
 
-  HAL_Report(HALUsageReporting::kResourceType_Compressor, pcmID + 1);
-  SendableRegistry::GetInstance().AddLW(this, "Compressor", pcmID);
+  HAL_Report(HALUsageReporting::kResourceType_Compressor, module + 1);
+  wpi::SendableRegistry::AddLW(this, "Compressor", module);
+}
+
+Compressor::Compressor(PneumaticsModuleType moduleType)
+    : Compressor{PneumaticsBase::GetDefaultForType(moduleType), moduleType} {}
+
+Compressor::~Compressor() {
+  m_module->UnreserveCompressor();
 }
 
 void Compressor::Start() {
-  if (StatusIsFatal()) return;
   SetClosedLoopControl(true);
 }
 
 void Compressor::Stop() {
-  if (StatusIsFatal()) return;
   SetClosedLoopControl(false);
 }
 
 bool Compressor::Enabled() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value = HAL_GetCompressor(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
+  return m_module->GetCompressor();
 }
 
 bool Compressor::GetPressureSwitchValue() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value = HAL_GetCompressorPressureSwitch(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
+  return m_module->GetPressureSwitch();
 }
 
 double Compressor::GetCompressorCurrent() const {
-  if (StatusIsFatal()) return 0;
-  int32_t status = 0;
-  double value;
-
-  value = HAL_GetCompressorCurrent(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
+  return m_module->GetCompressorCurrent();
 }
 
 void Compressor::SetClosedLoopControl(bool on) {
-  if (StatusIsFatal()) return;
-  int32_t status = 0;
-
-  HAL_SetCompressorClosedLoopControl(m_compressorHandle, on, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
+  m_module->SetClosedLoopControl(on);
 }
 
 bool Compressor::GetClosedLoopControl() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value = HAL_GetCompressorClosedLoopControl(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
+  return m_module->GetClosedLoopControl();
 }
 
-bool Compressor::GetCompressorCurrentTooHighFault() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value = HAL_GetCompressorCurrentTooHighFault(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
-}
-
-bool Compressor::GetCompressorCurrentTooHighStickyFault() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value =
-      HAL_GetCompressorCurrentTooHighStickyFault(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
-}
-
-bool Compressor::GetCompressorShortedStickyFault() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value = HAL_GetCompressorShortedStickyFault(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
-}
-
-bool Compressor::GetCompressorShortedFault() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value = HAL_GetCompressorShortedFault(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
-}
-
-bool Compressor::GetCompressorNotConnectedStickyFault() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value = HAL_GetCompressorNotConnectedStickyFault(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
-}
-
-bool Compressor::GetCompressorNotConnectedFault() const {
-  if (StatusIsFatal()) return false;
-  int32_t status = 0;
-  bool value;
-
-  value = HAL_GetCompressorNotConnectedFault(m_compressorHandle, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-
-  return value;
-}
-
-void Compressor::ClearAllPCMStickyFaults() {
-  if (StatusIsFatal()) return;
-  int32_t status = 0;
-
-  HAL_ClearAllPCMStickyFaults(m_module, &status);
-
-  if (status) {
-    wpi_setWPIError(Timeout);
-  }
-}
-
-int Compressor::GetModule() const { return m_module; }
-
-void Compressor::InitSendable(SendableBuilder& builder) {
+void Compressor::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Compressor");
   builder.AddBooleanProperty(
-      "Enabled", [=]() { return Enabled(); },
-      [=](bool value) {
-        if (value)
-          Start();
-        else
-          Stop();
-      });
+      "Closed Loop Control", [=]() { return GetClosedLoopControl(); },
+      [=](bool value) { SetClosedLoopControl(value); });
+  builder.AddBooleanProperty(
+      "Enabled", [=] { return Enabled(); }, nullptr);
   builder.AddBooleanProperty(
       "Pressure switch", [=]() { return GetPressureSwitchValue(); }, nullptr);
 }
diff --git a/wpilibc/src/main/native/cpp/Counter.cpp b/wpilibc/src/main/native/cpp/Counter.cpp
index ca74ab8..a1ad9e7 100644
--- a/wpilibc/src/main/native/cpp/Counter.cpp
+++ b/wpilibc/src/main/native/cpp/Counter.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Counter.h"
 
@@ -11,25 +8,26 @@
 
 #include <hal/Counter.h>
 #include <hal/FRCUsageReporting.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 #include "frc/AnalogTrigger.h"
-#include "frc/Base.h"
 #include "frc/DigitalInput.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
 Counter::Counter(Mode mode) {
   int32_t status = 0;
-  m_counter = HAL_InitializeCounter((HAL_Counter_Mode)mode, &m_index, &status);
-  wpi_setHALError(status);
+  m_counter = HAL_InitializeCounter(static_cast<HAL_Counter_Mode>(mode),
+                                    &m_index, &status);
+  FRC_CheckErrorStatus(status, "{}", "InitializeCounter");
 
-  SetMaxPeriod(0.5);
+  SetMaxPeriod(0.5_s);
 
   HAL_Report(HALUsageReporting::kResourceType_Counter, m_index + 1, mode + 1);
-  SendableRegistry::GetInstance().AddLW(this, "Counter", m_index);
+  wpi::SendableRegistry::AddLW(this, "Counter", m_index);
 }
 
 Counter::Counter(int channel) : Counter(kTwoPulse) {
@@ -56,9 +54,9 @@
                  DigitalSource* downSource, bool inverted)
     : Counter(encodingType,
               std::shared_ptr<DigitalSource>(upSource,
-                                             NullDeleter<DigitalSource>()),
+                                             wpi::NullDeleter<DigitalSource>()),
               std::shared_ptr<DigitalSource>(downSource,
-                                             NullDeleter<DigitalSource>()),
+                                             wpi::NullDeleter<DigitalSource>()),
               inverted) {}
 
 Counter::Counter(EncodingType encodingType,
@@ -66,10 +64,8 @@
                  std::shared_ptr<DigitalSource> downSource, bool inverted)
     : Counter(kExternalDirection) {
   if (encodingType != k1X && encodingType != k2X) {
-    wpi_setWPIErrorWithContext(
-        ParameterOutOfRange,
-        "Counter only supports 1X and 2X quadrature decoding.");
-    return;
+    throw FRC_MakeError(err::ParameterOutOfRange, "{}",
+                        "Counter only supports 1X and 2X quadrature decoding");
   }
   SetUpSource(upSource);
   SetDownSource(downSource);
@@ -83,256 +79,235 @@
     HAL_SetCounterAverageSize(m_counter, 2, &status);
   }
 
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Counter constructor");
   SetDownSourceEdge(inverted, true);
 }
 
 Counter::~Counter() {
-  SetUpdateWhenEmpty(true);
+  try {
+    SetUpdateWhenEmpty(true);
+  } catch (const RuntimeError& e) {
+    e.Report();
+  }
 
   int32_t status = 0;
   HAL_FreeCounter(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_ReportError(status, "{}", "Counter destructor");
 }
 
 void Counter::SetUpSource(int channel) {
-  if (StatusIsFatal()) return;
   SetUpSource(std::make_shared<DigitalInput>(channel));
-  SendableRegistry::GetInstance().AddChild(this, m_upSource.get());
+  wpi::SendableRegistry::AddChild(this, m_upSource.get());
 }
 
 void Counter::SetUpSource(AnalogTrigger* analogTrigger,
                           AnalogTriggerType triggerType) {
   SetUpSource(std::shared_ptr<AnalogTrigger>(analogTrigger,
-                                             NullDeleter<AnalogTrigger>()),
+                                             wpi::NullDeleter<AnalogTrigger>()),
               triggerType);
 }
 
 void Counter::SetUpSource(std::shared_ptr<AnalogTrigger> analogTrigger,
                           AnalogTriggerType triggerType) {
-  if (StatusIsFatal()) return;
   SetUpSource(analogTrigger->CreateOutput(triggerType));
 }
 
 void Counter::SetUpSource(DigitalSource* source) {
-  SetUpSource(
-      std::shared_ptr<DigitalSource>(source, NullDeleter<DigitalSource>()));
+  SetUpSource(std::shared_ptr<DigitalSource>(
+      source, wpi::NullDeleter<DigitalSource>()));
 }
 
 void Counter::SetUpSource(std::shared_ptr<DigitalSource> source) {
-  if (StatusIsFatal()) return;
   m_upSource = source;
-  if (m_upSource->StatusIsFatal()) {
-    CloneError(*m_upSource);
-  } else {
-    int32_t status = 0;
-    HAL_SetCounterUpSource(
-        m_counter, source->GetPortHandleForRouting(),
-        (HAL_AnalogTriggerType)source->GetAnalogTriggerTypeForRouting(),
-        &status);
-    wpi_setHALError(status);
-  }
+  int32_t status = 0;
+  HAL_SetCounterUpSource(m_counter, source->GetPortHandleForRouting(),
+                         static_cast<HAL_AnalogTriggerType>(
+                             source->GetAnalogTriggerTypeForRouting()),
+                         &status);
+  FRC_CheckErrorStatus(status, "{}", "SetUpSource");
 }
 
 void Counter::SetUpSource(DigitalSource& source) {
-  SetUpSource(
-      std::shared_ptr<DigitalSource>(&source, NullDeleter<DigitalSource>()));
+  SetUpSource(std::shared_ptr<DigitalSource>(
+      &source, wpi::NullDeleter<DigitalSource>()));
 }
 
 void Counter::SetUpSourceEdge(bool risingEdge, bool fallingEdge) {
-  if (StatusIsFatal()) return;
   if (m_upSource == nullptr) {
-    wpi_setWPIErrorWithContext(
-        NullParameter,
+    throw FRC_MakeError(
+        err::NullParameter, "{}",
         "Must set non-nullptr UpSource before setting UpSourceEdge");
   }
   int32_t status = 0;
   HAL_SetCounterUpSourceEdge(m_counter, risingEdge, fallingEdge, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetUpSourceEdge");
 }
 
 void Counter::ClearUpSource() {
-  if (StatusIsFatal()) return;
   m_upSource.reset();
   int32_t status = 0;
   HAL_ClearCounterUpSource(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "ClearUpSource");
 }
 
 void Counter::SetDownSource(int channel) {
-  if (StatusIsFatal()) return;
   SetDownSource(std::make_shared<DigitalInput>(channel));
-  SendableRegistry::GetInstance().AddChild(this, m_downSource.get());
+  wpi::SendableRegistry::AddChild(this, m_downSource.get());
 }
 
 void Counter::SetDownSource(AnalogTrigger* analogTrigger,
                             AnalogTriggerType triggerType) {
-  SetDownSource(std::shared_ptr<AnalogTrigger>(analogTrigger,
-                                               NullDeleter<AnalogTrigger>()),
+  SetDownSource(std::shared_ptr<AnalogTrigger>(
+                    analogTrigger, wpi::NullDeleter<AnalogTrigger>()),
                 triggerType);
 }
 
 void Counter::SetDownSource(std::shared_ptr<AnalogTrigger> analogTrigger,
                             AnalogTriggerType triggerType) {
-  if (StatusIsFatal()) return;
   SetDownSource(analogTrigger->CreateOutput(triggerType));
 }
 
 void Counter::SetDownSource(DigitalSource* source) {
-  SetDownSource(
-      std::shared_ptr<DigitalSource>(source, NullDeleter<DigitalSource>()));
+  SetDownSource(std::shared_ptr<DigitalSource>(
+      source, wpi::NullDeleter<DigitalSource>()));
 }
 
 void Counter::SetDownSource(DigitalSource& source) {
-  SetDownSource(
-      std::shared_ptr<DigitalSource>(&source, NullDeleter<DigitalSource>()));
+  SetDownSource(std::shared_ptr<DigitalSource>(
+      &source, wpi::NullDeleter<DigitalSource>()));
 }
 
 void Counter::SetDownSource(std::shared_ptr<DigitalSource> source) {
-  if (StatusIsFatal()) return;
   m_downSource = source;
-  if (m_downSource->StatusIsFatal()) {
-    CloneError(*m_downSource);
-  } else {
-    int32_t status = 0;
-    HAL_SetCounterDownSource(
-        m_counter, source->GetPortHandleForRouting(),
-        (HAL_AnalogTriggerType)source->GetAnalogTriggerTypeForRouting(),
-        &status);
-    wpi_setHALError(status);
-  }
+  int32_t status = 0;
+  HAL_SetCounterDownSource(m_counter, source->GetPortHandleForRouting(),
+                           static_cast<HAL_AnalogTriggerType>(
+                               source->GetAnalogTriggerTypeForRouting()),
+                           &status);
+  FRC_CheckErrorStatus(status, "{}", "SetDownSource");
 }
 
 void Counter::SetDownSourceEdge(bool risingEdge, bool fallingEdge) {
-  if (StatusIsFatal()) return;
   if (m_downSource == nullptr) {
-    wpi_setWPIErrorWithContext(
-        NullParameter,
+    throw FRC_MakeError(
+        err::NullParameter, "{}",
         "Must set non-nullptr DownSource before setting DownSourceEdge");
   }
   int32_t status = 0;
   HAL_SetCounterDownSourceEdge(m_counter, risingEdge, fallingEdge, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetDownSourceEdge");
 }
 
 void Counter::ClearDownSource() {
-  if (StatusIsFatal()) return;
   m_downSource.reset();
   int32_t status = 0;
   HAL_ClearCounterDownSource(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "ClearDownSource");
 }
 
 void Counter::SetUpDownCounterMode() {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetCounterUpDownMode(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetUpDownCounterMode");
 }
 
 void Counter::SetExternalDirectionMode() {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetCounterExternalDirectionMode(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetExternalDirectionMode");
 }
 
 void Counter::SetSemiPeriodMode(bool highSemiPeriod) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetCounterSemiPeriodMode(m_counter, highSemiPeriod, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSemiPeriodMode to {}",
+                       highSemiPeriod ? "true" : "false");
 }
 
 void Counter::SetPulseLengthMode(double threshold) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetCounterPulseLengthMode(m_counter, threshold, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetPulseLengthMode");
 }
 
 void Counter::SetReverseDirection(bool reverseDirection) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetCounterReverseDirection(m_counter, reverseDirection, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetReverseDirection to {}",
+                       reverseDirection ? "true" : "false");
 }
 
 void Counter::SetSamplesToAverage(int samplesToAverage) {
   if (samplesToAverage < 1 || samplesToAverage > 127) {
-    wpi_setWPIErrorWithContext(
-        ParameterOutOfRange,
-        "Average counter values must be between 1 and 127");
+    throw FRC_MakeError(
+        err::ParameterOutOfRange,
+        "Average counter values must be between 1 and 127, {} out of range",
+        samplesToAverage);
   }
   int32_t status = 0;
   HAL_SetCounterSamplesToAverage(m_counter, samplesToAverage, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSamplesToAverage to {}", samplesToAverage);
 }
 
 int Counter::GetSamplesToAverage() const {
   int32_t status = 0;
   int samples = HAL_GetCounterSamplesToAverage(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetSamplesToAverage");
   return samples;
 }
 
-int Counter::GetFPGAIndex() const { return m_index; }
+int Counter::GetFPGAIndex() const {
+  return m_index;
+}
 
 int Counter::Get() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int value = HAL_GetCounter(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Get");
   return value;
 }
 
 void Counter::Reset() {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_ResetCounter(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Reset");
 }
 
-double Counter::GetPeriod() const {
-  if (StatusIsFatal()) return 0.0;
+units::second_t Counter::GetPeriod() const {
   int32_t status = 0;
   double value = HAL_GetCounterPeriod(m_counter, &status);
-  wpi_setHALError(status);
-  return value;
+  FRC_CheckErrorStatus(status, "{}", "GetPeriod");
+  return units::second_t{value};
 }
 
-void Counter::SetMaxPeriod(double maxPeriod) {
-  if (StatusIsFatal()) return;
+void Counter::SetMaxPeriod(units::second_t maxPeriod) {
   int32_t status = 0;
-  HAL_SetCounterMaxPeriod(m_counter, maxPeriod, &status);
-  wpi_setHALError(status);
+  HAL_SetCounterMaxPeriod(m_counter, maxPeriod.value(), &status);
+  FRC_CheckErrorStatus(status, "{}", "SetMaxPeriod");
 }
 
 void Counter::SetUpdateWhenEmpty(bool enabled) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetCounterUpdateWhenEmpty(m_counter, enabled, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetUpdateWhenEmpty");
 }
 
 bool Counter::GetStopped() const {
-  if (StatusIsFatal()) return false;
   int32_t status = 0;
   bool value = HAL_GetCounterStopped(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetStopped");
   return value;
 }
 
 bool Counter::GetDirection() const {
-  if (StatusIsFatal()) return false;
   int32_t status = 0;
   bool value = HAL_GetCounterDirection(m_counter, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetDirection");
   return value;
 }
 
-void Counter::InitSendable(SendableBuilder& builder) {
+void Counter::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Counter");
   builder.AddDoubleProperty(
-      "Value", [=]() { return Get(); }, nullptr);
+      "Value", [=] { return Get(); }, nullptr);
 }
diff --git a/wpilibc/src/main/native/cpp/DMA.cpp b/wpilibc/src/main/native/cpp/DMA.cpp
index 1861555..80c0adb 100644
--- a/wpilibc/src/main/native/cpp/DMA.cpp
+++ b/wpilibc/src/main/native/cpp/DMA.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/DMA.h"
 
@@ -12,104 +9,154 @@
 #include <frc/DigitalSource.h>
 #include <frc/DutyCycle.h>
 #include <frc/Encoder.h>
+#include <frc/PWM.h>
+#include <frc/motorcontrol/PWMMotorController.h>
 
 #include <hal/DMA.h>
 #include <hal/HALBase.h>
 
+#include "frc/AnalogInput.h"
+#include "frc/Counter.h"
+#include "frc/DigitalSource.h"
+#include "frc/DutyCycle.h"
+#include "frc/Encoder.h"
+#include "frc/Errors.h"
+
 using namespace frc;
 
 DMA::DMA() {
   int32_t status = 0;
   dmaHandle = HAL_InitializeDMA(&status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "InitializeDMA");
 }
 
-DMA::~DMA() { HAL_FreeDMA(dmaHandle); }
+DMA::~DMA() {
+  HAL_FreeDMA(dmaHandle);
+}
 
 void DMA::SetPause(bool pause) {
   int32_t status = 0;
   HAL_SetDMAPause(dmaHandle, pause, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "SetPause");
 }
 
-void DMA::SetRate(int cycles) {
+void DMA::SetTimedTrigger(units::second_t seconds) {
   int32_t status = 0;
-  HAL_SetDMARate(dmaHandle, cycles, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  HAL_SetDMATimedTrigger(dmaHandle, seconds.value(), &status);
+  FRC_CheckErrorStatus(status, "{}", "SetTimedTrigger");
+}
+
+void DMA::SetTimedTriggerCycles(int cycles) {
+  int32_t status = 0;
+  HAL_SetDMATimedTriggerCycles(dmaHandle, cycles, &status);
+  FRC_CheckErrorStatus(status, "{}", "SetTimedTriggerCycles");
 }
 
 void DMA::AddEncoder(const Encoder* encoder) {
   int32_t status = 0;
   HAL_AddDMAEncoder(dmaHandle, encoder->m_encoder, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddEncoder");
 }
 
 void DMA::AddEncoderPeriod(const Encoder* encoder) {
   int32_t status = 0;
   HAL_AddDMAEncoderPeriod(dmaHandle, encoder->m_encoder, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddEncoderPeriod");
 }
 
 void DMA::AddCounter(const Counter* counter) {
   int32_t status = 0;
   HAL_AddDMACounter(dmaHandle, counter->m_counter, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddCounter");
 }
 
 void DMA::AddCounterPeriod(const Counter* counter) {
   int32_t status = 0;
   HAL_AddDMACounterPeriod(dmaHandle, counter->m_counter, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddCounterPeriod");
 }
 
 void DMA::AddDigitalSource(const DigitalSource* digitalSource) {
   int32_t status = 0;
   HAL_AddDMADigitalSource(dmaHandle, digitalSource->GetPortHandleForRouting(),
                           &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddDigitalSource");
 }
 
 void DMA::AddDutyCycle(const DutyCycle* dutyCycle) {
   int32_t status = 0;
   HAL_AddDMADutyCycle(dmaHandle, dutyCycle->m_handle, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddDutyCycle");
 }
 
 void DMA::AddAnalogInput(const AnalogInput* analogInput) {
   int32_t status = 0;
   HAL_AddDMAAnalogInput(dmaHandle, analogInput->m_port, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddAnalogInput");
 }
 
 void DMA::AddAveragedAnalogInput(const AnalogInput* analogInput) {
   int32_t status = 0;
   HAL_AddDMAAveragedAnalogInput(dmaHandle, analogInput->m_port, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddAveragedAnalogInput");
 }
 
 void DMA::AddAnalogAccumulator(const AnalogInput* analogInput) {
   int32_t status = 0;
   HAL_AddDMAAnalogAccumulator(dmaHandle, analogInput->m_port, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "AddAnalogAccumulator");
 }
 
-void DMA::SetExternalTrigger(DigitalSource* source, bool rising, bool falling) {
+int DMA::SetExternalTrigger(DigitalSource* source, bool rising, bool falling) {
   int32_t status = 0;
-  HAL_SetDMAExternalTrigger(dmaHandle, source->GetPortHandleForRouting(),
-                            static_cast<HAL_AnalogTriggerType>(
-                                source->GetAnalogTriggerTypeForRouting()),
-                            rising, falling, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  int32_t idx =
+      HAL_SetDMAExternalTrigger(dmaHandle, source->GetPortHandleForRouting(),
+                                static_cast<HAL_AnalogTriggerType>(
+                                    source->GetAnalogTriggerTypeForRouting()),
+                                rising, falling, &status);
+  FRC_CheckErrorStatus(status, "{}", "SetExternalTrigger");
+  return idx;
 }
 
-void DMA::StartDMA(int queueDepth) {
+int DMA::SetPwmEdgeTrigger(PWMMotorController* source, bool rising,
+                           bool falling) {
+  int32_t status = 0;
+  int32_t idx = HAL_SetDMAExternalTrigger(
+      dmaHandle, source->GetPwm()->m_handle,
+      HAL_AnalogTriggerType::HAL_Trigger_kInWindow, rising, falling, &status);
+  FRC_CheckErrorStatus(status, "{}", "SetPWmEdgeTrigger");
+  return idx;
+}
+
+int DMA::SetPwmEdgeTrigger(PWM* source, bool rising, bool falling) {
+  int32_t status = 0;
+  int32_t idx = HAL_SetDMAExternalTrigger(
+      dmaHandle, source->m_handle, HAL_AnalogTriggerType::HAL_Trigger_kInWindow,
+      rising, falling, &status);
+  FRC_CheckErrorStatus(status, "{}", "SetPWmEdgeTrigger");
+  return idx;
+}
+
+void DMA::ClearSensors() {
+  int32_t status = 0;
+  HAL_ClearDMASensors(dmaHandle, &status);
+  FRC_CheckErrorStatus(status, "{}", "ClearSensors");
+}
+
+void DMA::ClearExternalTriggers() {
+  int32_t status = 0;
+  HAL_ClearDMAExternalTriggers(dmaHandle, &status);
+  FRC_CheckErrorStatus(status, "{}", "ClearExternalTriggers");
+}
+
+void DMA::Start(int queueDepth) {
   int32_t status = 0;
   HAL_StartDMA(dmaHandle, queueDepth, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "Start");
 }
 
-void DMA::StopDMA() {
+void DMA::Stop() {
   int32_t status = 0;
   HAL_StopDMA(dmaHandle, &status);
-  wpi_setErrorWithContext(status, HAL_GetErrorMessage(status));
+  FRC_CheckErrorStatus(status, "{}", "Stop");
 }
diff --git a/wpilibc/src/main/native/cpp/DMC60.cpp b/wpilibc/src/main/native/cpp/DMC60.cpp
deleted file mode 100644
index e7d2c65..0000000
--- a/wpilibc/src/main/native/cpp/DMC60.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/DMC60.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-DMC60::DMC60(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_DigilentDMC60, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "DMC60", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/DSControlWord.cpp b/wpilibc/src/main/native/cpp/DSControlWord.cpp
new file mode 100644
index 0000000..15d209a
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/DSControlWord.cpp
@@ -0,0 +1,55 @@
+// 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.
+
+#include "frc/DSControlWord.h"
+
+#include <hal/DriverStation.h>
+
+using namespace frc;
+
+DSControlWord::DSControlWord() {
+  HAL_GetControlWord(&m_controlWord);
+}
+
+bool DSControlWord::IsEnabled() const {
+  return m_controlWord.enabled && m_controlWord.dsAttached;
+}
+
+bool DSControlWord::IsDisabled() const {
+  return !(m_controlWord.enabled && m_controlWord.dsAttached);
+}
+
+bool DSControlWord::IsEStopped() const {
+  return m_controlWord.eStop;
+}
+
+bool DSControlWord::IsAutonomous() const {
+  return m_controlWord.autonomous;
+}
+
+bool DSControlWord::IsAutonomousEnabled() const {
+  return m_controlWord.autonomous && m_controlWord.enabled &&
+         m_controlWord.dsAttached;
+}
+
+bool DSControlWord::IsTeleop() const {
+  return !(m_controlWord.autonomous || m_controlWord.test);
+}
+
+bool DSControlWord::IsTeleopEnabled() const {
+  return !m_controlWord.autonomous && !m_controlWord.test &&
+         m_controlWord.enabled && m_controlWord.dsAttached;
+}
+
+bool DSControlWord::IsTest() const {
+  return m_controlWord.test;
+}
+
+bool DSControlWord::IsDSAttached() const {
+  return m_controlWord.dsAttached;
+}
+
+bool DSControlWord::IsFMSAttached() const {
+  return m_controlWord.fmsAttached;
+}
diff --git a/wpilibc/src/main/native/cpp/Debouncer.cpp b/wpilibc/src/main/native/cpp/Debouncer.cpp
new file mode 100644
index 0000000..eb402cd
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/Debouncer.cpp
@@ -0,0 +1,37 @@
+// 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.
+
+#include "frc/Debouncer.h"
+
+using namespace frc;
+
+Debouncer::Debouncer(units::second_t debounceTime, DebounceType type)
+    : m_debounceTime(debounceTime), m_debounceType(type) {
+  switch (type) {
+    case DebounceType::kBoth:  // fall-through
+    case DebounceType::kRising:
+      m_baseline = false;
+      break;
+    case DebounceType::kFalling:
+      m_baseline = true;
+      break;
+  }
+  m_timer.Start();
+}
+
+bool Debouncer::Calculate(bool input) {
+  if (input == m_baseline) {
+    m_timer.Reset();
+  }
+
+  if (m_timer.HasElapsed(m_debounceTime)) {
+    if (m_debounceType == DebounceType::kBoth) {
+      m_baseline = input;
+      m_timer.Reset();
+    }
+    return input;
+  } else {
+    return m_baseline;
+  }
+}
diff --git a/wpilibc/src/main/native/cpp/DigitalGlitchFilter.cpp b/wpilibc/src/main/native/cpp/DigitalGlitchFilter.cpp
index 3cec3f1..3294a99 100644
--- a/wpilibc/src/main/native/cpp/DigitalGlitchFilter.cpp
+++ b/wpilibc/src/main/native/cpp/DigitalGlitchFilter.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/DigitalGlitchFilter.h"
 
@@ -14,13 +11,12 @@
 #include <hal/Constants.h>
 #include <hal/DIO.h>
 #include <hal/FRCUsageReporting.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 #include "frc/Counter.h"
 #include "frc/Encoder.h"
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
-#include "frc/Utility.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
@@ -32,15 +28,14 @@
   std::scoped_lock lock(m_mutex);
   auto index =
       std::find(m_filterAllocated.begin(), m_filterAllocated.end(), false);
-  wpi_assert(index != m_filterAllocated.end());
+  FRC_Assert(index != m_filterAllocated.end());
 
   m_channelIndex = std::distance(m_filterAllocated.begin(), index);
   *index = true;
 
   HAL_Report(HALUsageReporting::kResourceType_DigitalGlitchFilter,
              m_channelIndex + 1);
-  SendableRegistry::GetInstance().AddLW(this, "DigitalGlitchFilter",
-                                        m_channelIndex);
+  wpi::SendableRegistry::AddLW(this, "DigitalGlitchFilter", m_channelIndex);
 }
 
 DigitalGlitchFilter::~DigitalGlitchFilter() {
@@ -50,20 +45,6 @@
   }
 }
 
-DigitalGlitchFilter::DigitalGlitchFilter(DigitalGlitchFilter&& rhs)
-    : ErrorBase(std::move(rhs)), SendableHelper(std::move(rhs)) {
-  std::swap(m_channelIndex, rhs.m_channelIndex);
-}
-
-DigitalGlitchFilter& DigitalGlitchFilter::operator=(DigitalGlitchFilter&& rhs) {
-  ErrorBase::operator=(std::move(rhs));
-  SendableHelper::operator=(std::move(rhs));
-
-  std::swap(m_channelIndex, rhs.m_channelIndex);
-
-  return *this;
-}
-
 void DigitalGlitchFilter::Add(DigitalSource* input) {
   DoAdd(input, m_channelIndex + 1);
 }
@@ -74,60 +55,50 @@
   if (input) {
     // We don't support GlitchFilters on AnalogTriggers.
     if (input->IsAnalogTrigger()) {
-      wpi_setErrorWithContext(
-          -1, "Analog Triggers not supported for DigitalGlitchFilters");
-      return;
+      throw FRC_MakeError(
+          -1, "{}", "Analog Triggers not supported for DigitalGlitchFilters");
     }
     int32_t status = 0;
     HAL_SetFilterSelect(input->GetPortHandleForRouting(), requestedIndex,
                         &status);
-    wpi_setHALError(status);
+    FRC_CheckErrorStatus(status, "requested index {}", requestedIndex);
 
     // Validate that we set it correctly.
     int actualIndex =
         HAL_GetFilterSelect(input->GetPortHandleForRouting(), &status);
-    wpi_assertEqual(actualIndex, requestedIndex);
+    FRC_CheckErrorStatus(status, "requested index {}", requestedIndex);
+    FRC_Assert(actualIndex == requestedIndex);
   }
 }
 
 void DigitalGlitchFilter::Add(Encoder* input) {
   Add(input->m_aSource.get());
-  if (StatusIsFatal()) {
-    return;
-  }
   Add(input->m_bSource.get());
 }
 
 void DigitalGlitchFilter::Add(Counter* input) {
   Add(input->m_upSource.get());
-  if (StatusIsFatal()) {
-    return;
-  }
   Add(input->m_downSource.get());
 }
 
-void DigitalGlitchFilter::Remove(DigitalSource* input) { DoAdd(input, 0); }
+void DigitalGlitchFilter::Remove(DigitalSource* input) {
+  DoAdd(input, 0);
+}
 
 void DigitalGlitchFilter::Remove(Encoder* input) {
   Remove(input->m_aSource.get());
-  if (StatusIsFatal()) {
-    return;
-  }
   Remove(input->m_bSource.get());
 }
 
 void DigitalGlitchFilter::Remove(Counter* input) {
   Remove(input->m_upSource.get());
-  if (StatusIsFatal()) {
-    return;
-  }
   Remove(input->m_downSource.get());
 }
 
 void DigitalGlitchFilter::SetPeriodCycles(int fpgaCycles) {
   int32_t status = 0;
   HAL_SetFilterPeriod(m_channelIndex, fpgaCycles, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channelIndex);
 }
 
 void DigitalGlitchFilter::SetPeriodNanoSeconds(uint64_t nanoseconds) {
@@ -135,27 +106,22 @@
   int fpgaCycles =
       nanoseconds * HAL_GetSystemClockTicksPerMicrosecond() / 4 / 1000;
   HAL_SetFilterPeriod(m_channelIndex, fpgaCycles, &status);
-
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channelIndex);
 }
 
 int DigitalGlitchFilter::GetPeriodCycles() {
   int32_t status = 0;
   int fpgaCycles = HAL_GetFilterPeriod(m_channelIndex, &status);
-
-  wpi_setHALError(status);
-
+  FRC_CheckErrorStatus(status, "Channel {}", m_channelIndex);
   return fpgaCycles;
 }
 
 uint64_t DigitalGlitchFilter::GetPeriodNanoSeconds() {
   int32_t status = 0;
   int fpgaCycles = HAL_GetFilterPeriod(m_channelIndex, &status);
-
-  wpi_setHALError(status);
-
+  FRC_CheckErrorStatus(status, "Channel {}", m_channelIndex);
   return static_cast<uint64_t>(fpgaCycles) * 1000L /
          static_cast<uint64_t>(HAL_GetSystemClockTicksPerMicrosecond() / 4);
 }
 
-void DigitalGlitchFilter::InitSendable(SendableBuilder&) {}
+void DigitalGlitchFilter::InitSendable(wpi::SendableBuilder&) {}
diff --git a/wpilibc/src/main/native/cpp/DigitalInput.cpp b/wpilibc/src/main/native/cpp/DigitalInput.cpp
index d82f570..7bce921 100644
--- a/wpilibc/src/main/native/cpp/DigitalInput.cpp
+++ b/wpilibc/src/main/native/cpp/DigitalInput.cpp
@@ -1,77 +1,74 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/DigitalInput.h"
 
+#include <iostream>
 #include <limits>
 
 #include <hal/DIO.h>
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <hal/Ports.h>
+#include <wpi/StackTrace.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
 DigitalInput::DigitalInput(int channel) {
   if (!SensorUtil::CheckDigitalChannel(channel)) {
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange,
-                               "Digital Channel " + wpi::Twine(channel));
-    m_channel = std::numeric_limits<int>::max();
-    return;
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}", channel);
   }
   m_channel = channel;
 
   int32_t status = 0;
-  m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), true, &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumDigitalChannels(), channel);
-    m_handle = HAL_kInvalidHandle;
-    m_channel = std::numeric_limits<int>::max();
-    return;
-  }
+  std::string stackTrace = wpi::GetStackTrace(1);
+  m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), true,
+                                   stackTrace.c_str(), &status);
+  FRC_CheckErrorStatus(status, "Channel {}", channel);
 
   HAL_Report(HALUsageReporting::kResourceType_DigitalInput, channel + 1);
-  SendableRegistry::GetInstance().AddLW(this, "DigitalInput", channel);
+  wpi::SendableRegistry::AddLW(this, "DigitalInput", channel);
 }
 
 DigitalInput::~DigitalInput() {
-  if (StatusIsFatal()) return;
   HAL_FreeDIOPort(m_handle);
 }
 
 bool DigitalInput::Get() const {
-  if (StatusIsFatal()) return false;
   int32_t status = 0;
   bool value = HAL_GetDIO(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return value;
 }
 
-HAL_Handle DigitalInput::GetPortHandleForRouting() const { return m_handle; }
-
-AnalogTriggerType DigitalInput::GetAnalogTriggerTypeForRouting() const {
-  return (AnalogTriggerType)0;
+HAL_Handle DigitalInput::GetPortHandleForRouting() const {
+  return m_handle;
 }
 
-bool DigitalInput::IsAnalogTrigger() const { return false; }
+AnalogTriggerType DigitalInput::GetAnalogTriggerTypeForRouting() const {
+  return static_cast<AnalogTriggerType>(0);
+}
+
+bool DigitalInput::IsAnalogTrigger() const {
+  return false;
+}
 
 void DigitalInput::SetSimDevice(HAL_SimDeviceHandle device) {
   HAL_SetDIOSimDevice(m_handle, device);
 }
 
-int DigitalInput::GetChannel() const { return m_channel; }
+int DigitalInput::GetChannel() const {
+  return m_channel;
+}
 
-void DigitalInput::InitSendable(SendableBuilder& builder) {
+void DigitalInput::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Digital Input");
   builder.AddBooleanProperty(
-      "Value", [=]() { return Get(); }, nullptr);
+      "Value", [=] { return Get(); }, nullptr);
 }
diff --git a/wpilibc/src/main/native/cpp/DigitalOutput.cpp b/wpilibc/src/main/native/cpp/DigitalOutput.cpp
index 0bdc57c..5810e30 100644
--- a/wpilibc/src/main/native/cpp/DigitalOutput.cpp
+++ b/wpilibc/src/main/native/cpp/DigitalOutput.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/DigitalOutput.h"
 
@@ -13,146 +10,138 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <hal/Ports.h>
+#include <wpi/StackTrace.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
 DigitalOutput::DigitalOutput(int channel) {
   m_pwmGenerator = HAL_kInvalidHandle;
   if (!SensorUtil::CheckDigitalChannel(channel)) {
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange,
-                               "Digital Channel " + wpi::Twine(channel));
-    m_channel = std::numeric_limits<int>::max();
-    return;
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}", channel);
   }
   m_channel = channel;
 
   int32_t status = 0;
-  m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), false, &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumDigitalChannels(), channel);
-    m_channel = std::numeric_limits<int>::max();
-    m_handle = HAL_kInvalidHandle;
-    return;
-  }
+  std::string stackTrace = wpi::GetStackTrace(1);
+  m_handle = HAL_InitializeDIOPort(HAL_GetPort(channel), false,
+                                   stackTrace.c_str(), &status);
+  FRC_CheckErrorStatus(status, "Channel {}", channel);
 
   HAL_Report(HALUsageReporting::kResourceType_DigitalOutput, channel + 1);
-  SendableRegistry::GetInstance().AddLW(this, "DigitalOutput", channel);
+  wpi::SendableRegistry::AddLW(this, "DigitalOutput", channel);
 }
 
 DigitalOutput::~DigitalOutput() {
-  if (StatusIsFatal()) return;
   // Disable the PWM in case it was running.
-  DisablePWM();
+  try {
+    DisablePWM();
+  } catch (const RuntimeError& e) {
+    e.Report();
+  }
 
   HAL_FreeDIOPort(m_handle);
 }
 
 void DigitalOutput::Set(bool value) {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
   HAL_SetDIO(m_handle, value, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 bool DigitalOutput::Get() const {
-  if (StatusIsFatal()) return false;
-
   int32_t status = 0;
   bool val = HAL_GetDIO(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return val;
 }
 
-HAL_Handle DigitalOutput::GetPortHandleForRouting() const { return m_handle; }
-
-AnalogTriggerType DigitalOutput::GetAnalogTriggerTypeForRouting() const {
-  return (AnalogTriggerType)0;
+HAL_Handle DigitalOutput::GetPortHandleForRouting() const {
+  return m_handle;
 }
 
-bool DigitalOutput::IsAnalogTrigger() const { return false; }
+AnalogTriggerType DigitalOutput::GetAnalogTriggerTypeForRouting() const {
+  return static_cast<AnalogTriggerType>(0);
+}
 
-int DigitalOutput::GetChannel() const { return m_channel; }
+bool DigitalOutput::IsAnalogTrigger() const {
+  return false;
+}
+
+int DigitalOutput::GetChannel() const {
+  return m_channel;
+}
 
 void DigitalOutput::Pulse(double length) {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
   HAL_Pulse(m_handle, length, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 bool DigitalOutput::IsPulsing() const {
-  if (StatusIsFatal()) return false;
-
   int32_t status = 0;
   bool value = HAL_IsPulsing(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return value;
 }
 
 void DigitalOutput::SetPWMRate(double rate) {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
   HAL_SetDigitalPWMRate(rate, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void DigitalOutput::EnablePWM(double initialDutyCycle) {
-  if (m_pwmGenerator != HAL_kInvalidHandle) return;
+  if (m_pwmGenerator != HAL_kInvalidHandle) {
+    return;
+  }
 
   int32_t status = 0;
 
-  if (StatusIsFatal()) return;
   m_pwmGenerator = HAL_AllocateDigitalPWM(&status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 
-  if (StatusIsFatal()) return;
   HAL_SetDigitalPWMDutyCycle(m_pwmGenerator, initialDutyCycle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 
-  if (StatusIsFatal()) return;
   HAL_SetDigitalPWMOutputChannel(m_pwmGenerator, m_channel, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void DigitalOutput::DisablePWM() {
-  if (StatusIsFatal()) return;
-  if (m_pwmGenerator == HAL_kInvalidHandle) return;
+  if (m_pwmGenerator == HAL_kInvalidHandle) {
+    return;
+  }
 
   int32_t status = 0;
 
   // Disable the output by routing to a dead bit.
   HAL_SetDigitalPWMOutputChannel(m_pwmGenerator, SensorUtil::kDigitalChannels,
                                  &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 
   HAL_FreeDigitalPWM(m_pwmGenerator, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 
   m_pwmGenerator = HAL_kInvalidHandle;
 }
 
 void DigitalOutput::UpdateDutyCycle(double dutyCycle) {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
   HAL_SetDigitalPWMDutyCycle(m_pwmGenerator, dutyCycle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void DigitalOutput::SetSimDevice(HAL_SimDeviceHandle device) {
   HAL_SetDIOSimDevice(m_handle, device);
 }
 
-void DigitalOutput::InitSendable(SendableBuilder& builder) {
+void DigitalOutput::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Digital Output");
   builder.AddBooleanProperty(
-      "Value", [=]() { return Get(); }, [=](bool value) { Set(value); });
+      "Value", [=] { return Get(); }, [=](bool value) { Set(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/DoubleSolenoid.cpp b/wpilibc/src/main/native/cpp/DoubleSolenoid.cpp
index 67eb0b7..212673a 100644
--- a/wpilibc/src/main/native/cpp/DoubleSolenoid.cpp
+++ b/wpilibc/src/main/native/cpp/DoubleSolenoid.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/DoubleSolenoid.h"
 
@@ -12,128 +9,92 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <hal/Ports.h>
-#include <hal/Solenoid.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
-DoubleSolenoid::DoubleSolenoid(int forwardChannel, int reverseChannel)
-    : DoubleSolenoid(SensorUtil::GetDefaultSolenoidModule(), forwardChannel,
-                     reverseChannel) {}
-
-DoubleSolenoid::DoubleSolenoid(int moduleNumber, int forwardChannel,
-                               int reverseChannel)
-    : SolenoidBase(moduleNumber),
-      m_forwardChannel(forwardChannel),
-      m_reverseChannel(reverseChannel) {
-  if (!SensorUtil::CheckSolenoidModule(m_moduleNumber)) {
-    wpi_setWPIErrorWithContext(ModuleIndexOutOfRange,
-                               "Solenoid Module " + wpi::Twine(m_moduleNumber));
-    return;
+DoubleSolenoid::DoubleSolenoid(int module, PneumaticsModuleType moduleType,
+                               int forwardChannel, int reverseChannel)
+    : m_module{PneumaticsBase::GetForType(module, moduleType)},
+      m_forwardChannel{forwardChannel},
+      m_reverseChannel{reverseChannel} {
+  if (!m_module->CheckSolenoidChannel(m_forwardChannel)) {
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}",
+                        m_forwardChannel);
   }
-  if (!SensorUtil::CheckSolenoidChannel(m_forwardChannel)) {
-    wpi_setWPIErrorWithContext(
-        ChannelIndexOutOfRange,
-        "Solenoid Channel " + wpi::Twine(m_forwardChannel));
-    return;
-  }
-  if (!SensorUtil::CheckSolenoidChannel(m_reverseChannel)) {
-    wpi_setWPIErrorWithContext(
-        ChannelIndexOutOfRange,
-        "Solenoid Channel " + wpi::Twine(m_reverseChannel));
-    return;
-  }
-  int32_t status = 0;
-  m_forwardHandle = HAL_InitializeSolenoidPort(
-      HAL_GetPortWithModule(moduleNumber, m_forwardChannel), &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumSolenoidChannels(),
-                             forwardChannel);
-    m_forwardHandle = HAL_kInvalidHandle;
-    m_reverseHandle = HAL_kInvalidHandle;
-    return;
+  if (!m_module->CheckSolenoidChannel(m_reverseChannel)) {
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}",
+                        m_reverseChannel);
   }
 
-  m_reverseHandle = HAL_InitializeSolenoidPort(
-      HAL_GetPortWithModule(moduleNumber, m_reverseChannel), &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumSolenoidChannels(),
-                             reverseChannel);
-    // free forward solenoid
-    HAL_FreeSolenoidPort(m_forwardHandle);
-    m_forwardHandle = HAL_kInvalidHandle;
-    m_reverseHandle = HAL_kInvalidHandle;
-    return;
-  }
+  m_forwardMask = 1 << forwardChannel;
+  m_reverseMask = 1 << reverseChannel;
+  m_mask = m_forwardMask | m_reverseMask;
 
-  m_forwardMask = 1 << m_forwardChannel;
-  m_reverseMask = 1 << m_reverseChannel;
+  int allocMask = m_module->CheckAndReserveSolenoids(m_mask);
+  if (allocMask != 0) {
+    if (allocMask == m_mask) {
+      throw FRC_MakeError(err::ResourceAlreadyAllocated, "Channels {} and {}",
+                          m_forwardChannel, m_reverseChannel);
+    } else if (allocMask == m_forwardMask) {
+      throw FRC_MakeError(err::ResourceAlreadyAllocated, "Channel {}",
+                          m_forwardChannel);
+    } else {
+      throw FRC_MakeError(err::ResourceAlreadyAllocated, "Channel {}",
+                          m_reverseChannel);
+    }
+  }
 
   HAL_Report(HALUsageReporting::kResourceType_Solenoid, m_forwardChannel + 1,
-             m_moduleNumber + 1);
+             m_module->GetModuleNumber() + 1);
   HAL_Report(HALUsageReporting::kResourceType_Solenoid, m_reverseChannel + 1,
-             m_moduleNumber + 1);
+             m_module->GetModuleNumber() + 1);
 
-  SendableRegistry::GetInstance().AddLW(this, "DoubleSolenoid", m_moduleNumber,
-                                        m_forwardChannel);
+  wpi::SendableRegistry::AddLW(this, "DoubleSolenoid",
+                               m_module->GetModuleNumber(), m_forwardChannel);
 }
 
+DoubleSolenoid::DoubleSolenoid(PneumaticsModuleType moduleType,
+                               int forwardChannel, int reverseChannel)
+    : DoubleSolenoid{PneumaticsBase::GetDefaultForType(moduleType), moduleType,
+                     forwardChannel, reverseChannel} {}
+
 DoubleSolenoid::~DoubleSolenoid() {
-  HAL_FreeSolenoidPort(m_forwardHandle);
-  HAL_FreeSolenoidPort(m_reverseHandle);
+  m_module->UnreserveSolenoids(m_mask);
 }
 
 void DoubleSolenoid::Set(Value value) {
-  if (StatusIsFatal()) return;
-
-  bool forward = false;
-  bool reverse = false;
+  int setValue = 0;
 
   switch (value) {
     case kOff:
-      forward = false;
-      reverse = false;
+      setValue = 0;
       break;
     case kForward:
-      forward = true;
-      reverse = false;
+      setValue = m_forwardMask;
       break;
     case kReverse:
-      forward = false;
-      reverse = true;
+      setValue = m_reverseMask;
       break;
   }
 
-  int fstatus = 0;
-  HAL_SetSolenoid(m_forwardHandle, forward, &fstatus);
-  int rstatus = 0;
-  HAL_SetSolenoid(m_reverseHandle, reverse, &rstatus);
-
-  wpi_setHALError(fstatus);
-  wpi_setHALError(rstatus);
+  m_module->SetSolenoids(m_mask, setValue);
 }
 
 DoubleSolenoid::Value DoubleSolenoid::Get() const {
-  if (StatusIsFatal()) return kOff;
+  auto values = m_module->GetSolenoids();
 
-  int fstatus = 0;
-  int rstatus = 0;
-  bool valueForward = HAL_GetSolenoid(m_forwardHandle, &fstatus);
-  bool valueReverse = HAL_GetSolenoid(m_reverseHandle, &rstatus);
-
-  wpi_setHALError(fstatus);
-  wpi_setHALError(rstatus);
-
-  if (valueForward) {
-    return kForward;
-  } else if (valueReverse) {
-    return kReverse;
+  if ((values & m_forwardMask) != 0) {
+    return Value::kForward;
+  } else if ((values & m_reverseMask) != 0) {
+    return Value::kReverse;
   } else {
-    return kOff;
+    return Value::kOff;
   }
 }
 
@@ -147,23 +108,29 @@
   }
 }
 
-bool DoubleSolenoid::IsFwdSolenoidBlackListed() const {
-  int blackList = GetPCMSolenoidBlackList(m_moduleNumber);
-  return (blackList & m_forwardMask) != 0;
+int DoubleSolenoid::GetFwdChannel() const {
+  return m_forwardChannel;
 }
 
-bool DoubleSolenoid::IsRevSolenoidBlackListed() const {
-  int blackList = GetPCMSolenoidBlackList(m_moduleNumber);
-  return (blackList & m_reverseMask) != 0;
+int DoubleSolenoid::GetRevChannel() const {
+  return m_reverseChannel;
 }
 
-void DoubleSolenoid::InitSendable(SendableBuilder& builder) {
+bool DoubleSolenoid::IsFwdSolenoidDisabled() const {
+  return (m_module->GetSolenoidDisabledList() & m_forwardMask) != 0;
+}
+
+bool DoubleSolenoid::IsRevSolenoidDisabled() const {
+  return (m_module->GetSolenoidDisabledList() & m_reverseMask) != 0;
+}
+
+void DoubleSolenoid::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Double Solenoid");
   builder.SetActuator(true);
-  builder.SetSafeState([=]() { Set(kOff); });
+  builder.SetSafeState([=] { Set(kOff); });
   builder.AddSmallStringProperty(
       "Value",
-      [=](wpi::SmallVectorImpl<char>& buf) -> wpi::StringRef {
+      [=](wpi::SmallVectorImpl<char>& buf) -> std::string_view {
         switch (Get()) {
           case kForward:
             return "Forward";
@@ -173,12 +140,13 @@
             return "Off";
         }
       },
-      [=](wpi::StringRef value) {
+      [=](std::string_view value) {
         Value lvalue = kOff;
-        if (value == "Forward")
+        if (value == "Forward") {
           lvalue = kForward;
-        else if (value == "Reverse")
+        } else if (value == "Reverse") {
           lvalue = kReverse;
+        }
         Set(lvalue);
       });
 }
diff --git a/wpilibc/src/main/native/cpp/DriverStation.cpp b/wpilibc/src/main/native/cpp/DriverStation.cpp
index dbb99f9..37fc65f 100644
--- a/wpilibc/src/main/native/cpp/DriverStation.cpp
+++ b/wpilibc/src/main/native/cpp/DriverStation.cpp
@@ -1,69 +1,163 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/DriverStation.h"
 
-#include <chrono>
+#include <stdint.h>
 
+#include <array>
+#include <atomic>
+#include <chrono>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <type_traits>
+
+#include <fmt/format.h>
 #include <hal/DriverStation.h>
+#include <hal/DriverStationTypes.h>
 #include <hal/HALBase.h>
 #include <hal/Power.h>
 #include <networktables/NetworkTable.h>
 #include <networktables/NetworkTableEntry.h>
 #include <networktables/NetworkTableInstance.h>
-#include <wpi/SmallString.h>
-#include <wpi/StringRef.h>
+#include <wpi/condition_variable.h>
+#include <wpi/mutex.h>
 
+#include "frc/Errors.h"
 #include "frc/MotorSafety.h"
 #include "frc/Timer.h"
-#include "frc/WPIErrors.h"
-
-namespace frc {
-
-class MatchDataSender {
- public:
-  std::shared_ptr<nt::NetworkTable> table;
-  nt::NetworkTableEntry typeMetadata;
-  nt::NetworkTableEntry gameSpecificMessage;
-  nt::NetworkTableEntry eventName;
-  nt::NetworkTableEntry matchNumber;
-  nt::NetworkTableEntry replayNumber;
-  nt::NetworkTableEntry matchType;
-  nt::NetworkTableEntry alliance;
-  nt::NetworkTableEntry station;
-  nt::NetworkTableEntry controlWord;
-
-  MatchDataSender() {
-    table = nt::NetworkTableInstance::GetDefault().GetTable("FMSInfo");
-    typeMetadata = table->GetEntry(".type");
-    typeMetadata.ForceSetString("FMSInfo");
-    gameSpecificMessage = table->GetEntry("GameSpecificMessage");
-    gameSpecificMessage.ForceSetString("");
-    eventName = table->GetEntry("EventName");
-    eventName.ForceSetString("");
-    matchNumber = table->GetEntry("MatchNumber");
-    matchNumber.ForceSetDouble(0);
-    replayNumber = table->GetEntry("ReplayNumber");
-    replayNumber.ForceSetDouble(0);
-    matchType = table->GetEntry("MatchType");
-    matchType.ForceSetDouble(0);
-    alliance = table->GetEntry("IsRedAlliance");
-    alliance.ForceSetBoolean(true);
-    station = table->GetEntry("StationNumber");
-    station.ForceSetDouble(1);
-    controlWord = table->GetEntry("FMSControlData");
-    controlWord.ForceSetDouble(0);
-  }
-};
-}  // namespace frc
 
 using namespace frc;
 
-static constexpr double kJoystickUnpluggedMessageInterval = 1.0;
+namespace {
+// A simple class which caches the previous value written to an NT entry
+// Used to prevent redundant, repeated writes of the same value
+template <class T>
+class MatchDataSenderEntry {
+ public:
+  MatchDataSenderEntry(const std::shared_ptr<nt::NetworkTable>& table,
+                       std::string_view key, const T& initialVal) {
+    static_assert(std::is_same_v<T, bool> || std::is_same_v<T, double> ||
+                      std::is_same_v<T, std::string>,
+                  "Invalid type for MatchDataSenderEntry - must be "
+                  "to bool, double or std::string");
+
+    ntEntry = table->GetEntry(key);
+    if constexpr (std::is_same_v<T, bool>) {
+      ntEntry.ForceSetBoolean(initialVal);
+    } else if constexpr (std::is_same_v<T, double>) {
+      ntEntry.ForceSetDouble(initialVal);
+    } else if constexpr (std::is_same_v<T, std::string>) {
+      ntEntry.ForceSetString(initialVal);
+    }
+    prevVal = initialVal;
+  }
+
+  void Set(const T& val) {
+    if (val != prevVal) {
+      SetValue(val);
+      prevVal = val;
+    }
+  }
+
+ private:
+  nt::NetworkTableEntry ntEntry;
+  T prevVal;
+
+  void SetValue(bool val) { ntEntry.SetBoolean(val); }
+  void SetValue(double val) { ntEntry.SetDouble(val); }
+  void SetValue(std::string_view val) { ntEntry.SetString(val); }
+};
+
+struct MatchDataSender {
+  std::shared_ptr<nt::NetworkTable> table =
+      nt::NetworkTableInstance::GetDefault().GetTable("FMSInfo");
+  MatchDataSenderEntry<std::string> typeMetaData{table, ".type", "FMSInfo"};
+  MatchDataSenderEntry<std::string> gameSpecificMessage{
+      table, "GameSpecificMessage", ""};
+  MatchDataSenderEntry<std::string> eventName{table, "EventName", ""};
+  MatchDataSenderEntry<double> matchNumber{table, "MatchNumber", 0.0};
+  MatchDataSenderEntry<double> replayNumber{table, "ReplayNumber", 0.0};
+  MatchDataSenderEntry<double> matchType{table, "MatchType", 0.0};
+  MatchDataSenderEntry<bool> alliance{table, "IsRedAlliance", true};
+  MatchDataSenderEntry<double> station{table, "StationNumber", 1.0};
+  MatchDataSenderEntry<double> controlWord{table, "FMSControlData", 0.0};
+};
+
+struct Instance {
+  Instance();
+  ~Instance();
+
+  MatchDataSender matchDataSender;
+
+  // Joystick button rising/falling edge flags
+  wpi::mutex buttonEdgeMutex;
+  std::array<HAL_JoystickButtons, DriverStation::kJoystickPorts>
+      previousButtonStates;
+  std::array<uint32_t, DriverStation::kJoystickPorts> joystickButtonsPressed;
+  std::array<uint32_t, DriverStation::kJoystickPorts> joystickButtonsReleased;
+
+  // Internal Driver Station thread
+  std::thread dsThread;
+  std::atomic<bool> isRunning{false};
+
+  mutable wpi::mutex waitForDataMutex;
+  wpi::condition_variable waitForDataCond;
+  int waitForDataCounter = 0;
+
+  bool silenceJoystickWarning = false;
+
+  // Robot state status variables
+  bool userInDisabled = false;
+  bool userInAutonomous = false;
+  bool userInTeleop = false;
+  bool userInTest = false;
+
+  units::second_t nextMessageTime = 0_s;
+};
+}  // namespace
+
+static constexpr auto kJoystickUnpluggedMessageInterval = 1_s;
+
+static Instance& GetInstance() {
+  static Instance instance;
+  return instance;
+}
+
+static void Run();
+static void SendMatchData();
+
+/**
+ * Reports errors related to unplugged joysticks.
+ *
+ * Throttles the errors so that they don't overwhelm the DS.
+ */
+static void ReportJoystickUnpluggedErrorV(fmt::string_view format,
+                                          fmt::format_args args);
+
+template <typename S, typename... Args>
+static inline void ReportJoystickUnpluggedError(const S& format,
+                                                Args&&... args) {
+  ReportJoystickUnpluggedErrorV(
+      format, fmt::make_args_checked<Args...>(format, args...));
+}
+
+/**
+ * Reports errors related to unplugged joysticks.
+ *
+ * Throttles the errors so that they don't overwhelm the DS.
+ */
+static void ReportJoystickUnpluggedWarningV(fmt::string_view format,
+                                            fmt::format_args args);
+
+template <typename S, typename... Args>
+static inline void ReportJoystickUnpluggedWarning(const S& format,
+                                                  Args&&... args) {
+  ReportJoystickUnpluggedWarningV(
+      format, fmt::make_args_checked<Args...>(format, args...));
+}
 
 static int& GetDSLastCount() {
   // There is a rollover error condition here. At Packet# = n * (uintmax), this
@@ -74,51 +168,42 @@
   return lastCount;
 }
 
-DriverStation::~DriverStation() {
-  m_isRunning = false;
+Instance::Instance() {
+  HAL_Initialize(500, 0);
+
+  // All joysticks should default to having zero axes, povs and buttons, so
+  // uninitialized memory doesn't get sent to motor controllers.
+  for (unsigned int i = 0; i < DriverStation::kJoystickPorts; i++) {
+    joystickButtonsPressed[i] = 0;
+    joystickButtonsReleased[i] = 0;
+    previousButtonStates[i].count = 0;
+    previousButtonStates[i].buttons = 0;
+  }
+
+  dsThread = std::thread(&Run);
+}
+
+Instance::~Instance() {
+  isRunning = false;
   // Trigger a DS mutex release in case there is no driver station running.
   HAL_ReleaseDSMutex();
-  m_dsThread.join();
+  dsThread.join();
 }
 
 DriverStation& DriverStation::GetInstance() {
+  ::GetInstance();
   static DriverStation instance;
   return instance;
 }
 
-void DriverStation::ReportError(const wpi::Twine& error) {
-  wpi::SmallString<128> temp;
-  HAL_SendError(1, 1, 0, error.toNullTerminatedStringRef(temp).data(), "", "",
-                1);
-}
-
-void DriverStation::ReportWarning(const wpi::Twine& error) {
-  wpi::SmallString<128> temp;
-  HAL_SendError(0, 1, 0, error.toNullTerminatedStringRef(temp).data(), "", "",
-                1);
-}
-
-void DriverStation::ReportError(bool isError, int32_t code,
-                                const wpi::Twine& error,
-                                const wpi::Twine& location,
-                                const wpi::Twine& stack) {
-  wpi::SmallString<128> errorTemp;
-  wpi::SmallString<128> locationTemp;
-  wpi::SmallString<128> stackTemp;
-  HAL_SendError(isError, code, 0,
-                error.toNullTerminatedStringRef(errorTemp).data(),
-                location.toNullTerminatedStringRef(locationTemp).data(),
-                stack.toNullTerminatedStringRef(stackTemp).data(), 1);
-}
-
 bool DriverStation::GetStickButton(int stick, int button) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return false;
   }
   if (button <= 0) {
     ReportJoystickUnpluggedError(
-        "ERROR: Button indexes begin at 1 in WPILib for C++ and Java");
+        "Joystick Button {} index out of range; indexes begin at 1", button);
     return false;
   }
 
@@ -127,7 +212,9 @@
 
   if (button > buttons.count) {
     ReportJoystickUnpluggedWarning(
-        "Joystick Button missing, check if all controllers are plugged in");
+        "Joystick Button {} missing (max {}), check if all controllers are "
+        "plugged in",
+        button, buttons.count);
     return false;
   }
 
@@ -136,12 +223,12 @@
 
 bool DriverStation::GetStickButtonPressed(int stick, int button) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return false;
   }
   if (button <= 0) {
     ReportJoystickUnpluggedError(
-        "ERROR: Button indexes begin at 1 in WPILib for C++ and Java");
+        "Joystick Button {} index out of range; indexes begin at 1", button);
     return false;
   }
 
@@ -150,27 +237,29 @@
 
   if (button > buttons.count) {
     ReportJoystickUnpluggedWarning(
-        "Joystick Button missing, check if all controllers are plugged in");
+        "Joystick Button {} missing (max {}), check if all controllers are "
+        "plugged in",
+        button, buttons.count);
     return false;
   }
-  std::unique_lock lock(m_buttonEdgeMutex);
+  auto& inst = ::GetInstance();
+  std::unique_lock lock(inst.buttonEdgeMutex);
   // If button was pressed, clear flag and return true
-  if (m_joystickButtonsPressed[stick] & 1 << (button - 1)) {
-    m_joystickButtonsPressed[stick] &= ~(1 << (button - 1));
+  if (inst.joystickButtonsPressed[stick] & 1 << (button - 1)) {
+    inst.joystickButtonsPressed[stick] &= ~(1 << (button - 1));
     return true;
-  } else {
-    return false;
   }
+  return false;
 }
 
 bool DriverStation::GetStickButtonReleased(int stick, int button) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return false;
   }
   if (button <= 0) {
     ReportJoystickUnpluggedError(
-        "ERROR: Button indexes begin at 1 in WPILib for C++ and Java");
+        "Joystick Button {} index out of range; indexes begin at 1", button);
     return false;
   }
 
@@ -179,26 +268,28 @@
 
   if (button > buttons.count) {
     ReportJoystickUnpluggedWarning(
-        "Joystick Button missing, check if all controllers are plugged in");
+        "Joystick Button {} missing (max {}), check if all controllers are "
+        "plugged in",
+        button, buttons.count);
     return false;
   }
-  std::unique_lock lock(m_buttonEdgeMutex);
+  auto& inst = ::GetInstance();
+  std::unique_lock lock(inst.buttonEdgeMutex);
   // If button was released, clear flag and return true
-  if (m_joystickButtonsReleased[stick] & 1 << (button - 1)) {
-    m_joystickButtonsReleased[stick] &= ~(1 << (button - 1));
+  if (inst.joystickButtonsReleased[stick] & 1 << (button - 1)) {
+    inst.joystickButtonsReleased[stick] &= ~(1 << (button - 1));
     return true;
-  } else {
-    return false;
   }
+  return false;
 }
 
 double DriverStation::GetStickAxis(int stick, int axis) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return 0.0;
   }
   if (axis < 0 || axis >= HAL_kMaxJoystickAxes) {
-    wpi_setWPIError(BadJoystickAxis);
+    FRC_ReportError(warn::BadJoystickAxis, "axis {} out of range", axis);
     return 0.0;
   }
 
@@ -207,7 +298,9 @@
 
   if (axis >= axes.count) {
     ReportJoystickUnpluggedWarning(
-        "Joystick Axis missing, check if all controllers are plugged in");
+        "Joystick Axis {} missing (max {}), check if all controllers are "
+        "plugged in",
+        axis, axes.count);
     return 0.0;
   }
 
@@ -216,11 +309,11 @@
 
 int DriverStation::GetStickPOV(int stick, int pov) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return -1;
   }
   if (pov < 0 || pov >= HAL_kMaxJoystickPOVs) {
-    wpi_setWPIError(BadJoystickAxis);
+    FRC_ReportError(warn::BadJoystickAxis, "POV {} out of range", pov);
     return -1;
   }
 
@@ -229,16 +322,18 @@
 
   if (pov >= povs.count) {
     ReportJoystickUnpluggedWarning(
-        "Joystick POV missing, check if all controllers are plugged in");
+        "Joystick POV {} missing (max {}), check if all controllers are "
+        "plugged in",
+        pov, povs.count);
     return -1;
   }
 
   return povs.povs[pov];
 }
 
-int DriverStation::GetStickButtons(int stick) const {
+int DriverStation::GetStickButtons(int stick) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return 0;
   }
 
@@ -248,9 +343,9 @@
   return buttons.buttons;
 }
 
-int DriverStation::GetStickAxisCount(int stick) const {
+int DriverStation::GetStickAxisCount(int stick) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return 0;
   }
 
@@ -260,9 +355,9 @@
   return axes.count;
 }
 
-int DriverStation::GetStickPOVCount(int stick) const {
+int DriverStation::GetStickPOVCount(int stick) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return 0;
   }
 
@@ -272,9 +367,9 @@
   return povs.count;
 }
 
-int DriverStation::GetStickButtonCount(int stick) const {
+int DriverStation::GetStickButtonCount(int stick) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return 0;
   }
 
@@ -284,9 +379,9 @@
   return buttons.count;
 }
 
-bool DriverStation::GetJoystickIsXbox(int stick) const {
+bool DriverStation::GetJoystickIsXbox(int stick) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return false;
   }
 
@@ -296,9 +391,9 @@
   return static_cast<bool>(descriptor.isXbox);
 }
 
-int DriverStation::GetJoystickType(int stick) const {
+int DriverStation::GetJoystickType(int stick) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return -1;
   }
 
@@ -308,9 +403,9 @@
   return static_cast<int>(descriptor.type);
 }
 
-std::string DriverStation::GetJoystickName(int stick) const {
+std::string DriverStation::GetJoystickName(int stick) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
   }
 
   HAL_JoystickDescriptor descriptor;
@@ -319,9 +414,9 @@
   return descriptor.name;
 }
 
-int DriverStation::GetJoystickAxisType(int stick, int axis) const {
+int DriverStation::GetJoystickAxisType(int stick, int axis) {
   if (stick < 0 || stick >= kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+    FRC_ReportError(warn::BadJoystickIndex, "stick {} out of range", stick);
     return -1;
   }
 
@@ -331,112 +426,123 @@
   return static_cast<bool>(descriptor.axisTypes);
 }
 
-bool DriverStation::IsJoystickConnected(int stick) const {
+bool DriverStation::IsJoystickConnected(int stick) {
   return GetStickAxisCount(stick) > 0 || GetStickButtonCount(stick) > 0 ||
          GetStickPOVCount(stick) > 0;
 }
 
-bool DriverStation::IsEnabled() const {
+bool DriverStation::IsEnabled() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return controlWord.enabled && controlWord.dsAttached;
 }
 
-bool DriverStation::IsDisabled() const {
+bool DriverStation::IsDisabled() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return !(controlWord.enabled && controlWord.dsAttached);
 }
 
-bool DriverStation::IsEStopped() const {
+bool DriverStation::IsEStopped() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return controlWord.eStop;
 }
 
-bool DriverStation::IsAutonomous() const {
+bool DriverStation::IsAutonomous() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return controlWord.autonomous;
 }
 
-bool DriverStation::IsAutonomousEnabled() const {
+bool DriverStation::IsAutonomousEnabled() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return controlWord.autonomous && controlWord.enabled;
 }
 
-bool DriverStation::IsOperatorControl() const {
+bool DriverStation::IsOperatorControl() {
+  return IsTeleop();
+}
+
+bool DriverStation::IsTeleop() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return !(controlWord.autonomous || controlWord.test);
 }
 
-bool DriverStation::IsOperatorControlEnabled() const {
+bool DriverStation::IsOperatorControlEnabled() {
+  return IsTeleopEnabled();
+}
+
+bool DriverStation::IsTeleopEnabled() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return !controlWord.autonomous && !controlWord.test && controlWord.enabled;
 }
 
-bool DriverStation::IsTest() const {
+bool DriverStation::IsTest() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return controlWord.test;
 }
 
-bool DriverStation::IsDSAttached() const {
+bool DriverStation::IsDSAttached() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return controlWord.dsAttached;
 }
 
-bool DriverStation::IsNewControlData() const {
-  std::unique_lock lock(m_waitForDataMutex);
+bool DriverStation::IsNewControlData() {
+  auto& inst = ::GetInstance();
+  std::unique_lock lock(inst.waitForDataMutex);
   int& lastCount = GetDSLastCount();
-  int currentCount = m_waitForDataCounter;
-  if (lastCount == currentCount) return false;
+  int currentCount = inst.waitForDataCounter;
+  if (lastCount == currentCount) {
+    return false;
+  }
   lastCount = currentCount;
   return true;
 }
 
-bool DriverStation::IsFMSAttached() const {
+bool DriverStation::IsFMSAttached() {
   HAL_ControlWord controlWord;
   HAL_GetControlWord(&controlWord);
   return controlWord.fmsAttached;
 }
 
-std::string DriverStation::GetGameSpecificMessage() const {
+std::string DriverStation::GetGameSpecificMessage() {
   HAL_MatchInfo info;
   HAL_GetMatchInfo(&info);
   return std::string(reinterpret_cast<char*>(info.gameSpecificMessage),
                      info.gameSpecificMessageSize);
 }
 
-std::string DriverStation::GetEventName() const {
+std::string DriverStation::GetEventName() {
   HAL_MatchInfo info;
   HAL_GetMatchInfo(&info);
   return info.eventName;
 }
 
-DriverStation::MatchType DriverStation::GetMatchType() const {
+DriverStation::MatchType DriverStation::GetMatchType() {
   HAL_MatchInfo info;
   HAL_GetMatchInfo(&info);
   return static_cast<DriverStation::MatchType>(info.matchType);
 }
 
-int DriverStation::GetMatchNumber() const {
+int DriverStation::GetMatchNumber() {
   HAL_MatchInfo info;
   HAL_GetMatchInfo(&info);
   return info.matchNumber;
 }
 
-int DriverStation::GetReplayNumber() const {
+int DriverStation::GetReplayNumber() {
   HAL_MatchInfo info;
   HAL_GetMatchInfo(&info);
   return info.replayNumber;
 }
 
-DriverStation::Alliance DriverStation::GetAlliance() const {
+DriverStation::Alliance DriverStation::GetAlliance() {
   int32_t status = 0;
   auto allianceStationID = HAL_GetAllianceStation(&status);
   switch (allianceStationID) {
@@ -453,7 +559,7 @@
   }
 }
 
-int DriverStation::GetLocation() const {
+int DriverStation::GetLocation() {
   int32_t status = 0;
   auto allianceStationID = HAL_GetAllianceStation(&status);
   switch (allianceStationID) {
@@ -471,143 +577,171 @@
   }
 }
 
-void DriverStation::WaitForData() { WaitForData(0); }
+void DriverStation::WaitForData() {
+  WaitForData(0_s);
+}
 
-bool DriverStation::WaitForData(double timeout) {
-  auto timeoutTime =
-      std::chrono::steady_clock::now() + std::chrono::duration<double>(timeout);
+bool DriverStation::WaitForData(units::second_t timeout) {
+  auto& inst = ::GetInstance();
+  auto timeoutTime = std::chrono::steady_clock::now() +
+                     std::chrono::steady_clock::duration{timeout};
 
-  std::unique_lock lock(m_waitForDataMutex);
+  std::unique_lock lock(inst.waitForDataMutex);
   int& lastCount = GetDSLastCount();
-  int currentCount = m_waitForDataCounter;
+  int currentCount = inst.waitForDataCounter;
   if (lastCount != currentCount) {
     lastCount = currentCount;
     return true;
   }
-  while (m_waitForDataCounter == currentCount) {
-    if (timeout > 0) {
-      auto timedOut = m_waitForDataCond.wait_until(lock, timeoutTime);
+  while (inst.waitForDataCounter == currentCount) {
+    if (timeout > 0_s) {
+      auto timedOut = inst.waitForDataCond.wait_until(lock, timeoutTime);
       if (timedOut == std::cv_status::timeout) {
         return false;
       }
     } else {
-      m_waitForDataCond.wait(lock);
+      inst.waitForDataCond.wait(lock);
     }
   }
-  lastCount = m_waitForDataCounter;
+  lastCount = inst.waitForDataCounter;
   return true;
 }
 
-double DriverStation::GetMatchTime() const {
-  int32_t status;
+double DriverStation::GetMatchTime() {
+  int32_t status = 0;
   return HAL_GetMatchTime(&status);
 }
 
-double DriverStation::GetBatteryVoltage() const {
+double DriverStation::GetBatteryVoltage() {
   int32_t status = 0;
   double voltage = HAL_GetVinVoltage(&status);
-  wpi_setErrorWithContext(status, "getVinVoltage");
+  FRC_CheckErrorStatus(status, "{}", "getVinVoltage");
 
   return voltage;
 }
 
-void DriverStation::WakeupWaitForData() {
-  std::scoped_lock waitLock(m_waitForDataMutex);
-  // Nofify all threads
-  m_waitForDataCounter++;
-  m_waitForDataCond.notify_all();
+void DriverStation::InDisabled(bool entering) {
+  ::GetInstance().userInDisabled = entering;
 }
 
-void DriverStation::GetData() {
+void DriverStation::InAutonomous(bool entering) {
+  ::GetInstance().userInAutonomous = entering;
+}
+
+void DriverStation::InOperatorControl(bool entering) {
+  InTeleop(entering);
+}
+
+void DriverStation::InTeleop(bool entering) {
+  ::GetInstance().userInTeleop = entering;
+}
+
+void DriverStation::InTest(bool entering) {
+  ::GetInstance().userInTest = entering;
+}
+
+void DriverStation::WakeupWaitForData() {
+  auto& inst = ::GetInstance();
+  std::scoped_lock waitLock(inst.waitForDataMutex);
+  // Nofify all threads
+  inst.waitForDataCounter++;
+  inst.waitForDataCond.notify_all();
+}
+
+/**
+ * Copy data from the DS task for the user.
+ *
+ * If no new data exists, it will just be returned, otherwise
+ * the data will be copied from the DS polling loop.
+ */
+void GetData() {
+  auto& inst = ::GetInstance();
   {
     // Compute the pressed and released buttons
     HAL_JoystickButtons currentButtons;
-    std::unique_lock lock(m_buttonEdgeMutex);
+    std::unique_lock lock(inst.buttonEdgeMutex);
 
-    for (int32_t i = 0; i < kJoystickPorts; i++) {
+    for (int32_t i = 0; i < DriverStation::kJoystickPorts; i++) {
       HAL_GetJoystickButtons(i, &currentButtons);
 
       // If buttons weren't pressed and are now, set flags in m_buttonsPressed
-      m_joystickButtonsPressed[i] |=
-          ~m_previousButtonStates[i].buttons & currentButtons.buttons;
+      inst.joystickButtonsPressed[i] |=
+          ~inst.previousButtonStates[i].buttons & currentButtons.buttons;
 
       // If buttons were pressed and aren't now, set flags in m_buttonsReleased
-      m_joystickButtonsReleased[i] |=
-          m_previousButtonStates[i].buttons & ~currentButtons.buttons;
+      inst.joystickButtonsReleased[i] |=
+          inst.previousButtonStates[i].buttons & ~currentButtons.buttons;
 
-      m_previousButtonStates[i] = currentButtons;
+      inst.previousButtonStates[i] = currentButtons;
     }
   }
 
-  WakeupWaitForData();
+  DriverStation::WakeupWaitForData();
   SendMatchData();
 }
 
 void DriverStation::SilenceJoystickConnectionWarning(bool silence) {
-  m_silenceJoystickWarning = silence;
+  ::GetInstance().silenceJoystickWarning = silence;
 }
 
-bool DriverStation::IsJoystickConnectionWarningSilenced() const {
-  return !IsFMSAttached() && m_silenceJoystickWarning;
+bool DriverStation::IsJoystickConnectionWarningSilenced() {
+  return !IsFMSAttached() && ::GetInstance().silenceJoystickWarning;
 }
 
-DriverStation::DriverStation() {
-  HAL_Initialize(500, 0);
-  m_waitForDataCounter = 0;
-
-  m_matchDataSender = std::make_unique<MatchDataSender>();
-
-  // All joysticks should default to having zero axes, povs and buttons, so
-  // uninitialized memory doesn't get sent to speed controllers.
-  for (unsigned int i = 0; i < kJoystickPorts; i++) {
-    m_joystickButtonsPressed[i] = 0;
-    m_joystickButtonsReleased[i] = 0;
-    m_previousButtonStates[i].count = 0;
-    m_previousButtonStates[i].buttons = 0;
-  }
-
-  m_dsThread = std::thread(&DriverStation::Run, this);
-}
-
-void DriverStation::ReportJoystickUnpluggedError(const wpi::Twine& message) {
-  double currentTime = Timer::GetFPGATimestamp();
-  if (currentTime > m_nextMessageTime) {
-    ReportError(message);
-    m_nextMessageTime = currentTime + kJoystickUnpluggedMessageInterval;
+void ReportJoystickUnpluggedErrorV(fmt::string_view format,
+                                   fmt::format_args args) {
+  auto& inst = GetInstance();
+  auto currentTime = Timer::GetFPGATimestamp();
+  if (currentTime > inst.nextMessageTime) {
+    ReportErrorV(err::Error, "", 0, "", format, args);
+    inst.nextMessageTime = currentTime + kJoystickUnpluggedMessageInterval;
   }
 }
 
-void DriverStation::ReportJoystickUnpluggedWarning(const wpi::Twine& message) {
-  if (IsFMSAttached() || !m_silenceJoystickWarning) {
-    double currentTime = Timer::GetFPGATimestamp();
-    if (currentTime > m_nextMessageTime) {
-      ReportWarning(message);
-      m_nextMessageTime = currentTime + kJoystickUnpluggedMessageInterval;
+void ReportJoystickUnpluggedWarningV(fmt::string_view format,
+                                     fmt::format_args args) {
+  auto& inst = GetInstance();
+  if (DriverStation::IsFMSAttached() || !inst.silenceJoystickWarning) {
+    auto currentTime = Timer::GetFPGATimestamp();
+    if (currentTime > inst.nextMessageTime) {
+      ReportErrorV(warn::Warning, "", 0, "", format, args);
+      inst.nextMessageTime = currentTime + kJoystickUnpluggedMessageInterval;
     }
   }
 }
 
-void DriverStation::Run() {
-  m_isRunning = true;
+void Run() {
+  auto& inst = GetInstance();
+  inst.isRunning = true;
   int safetyCounter = 0;
-  while (m_isRunning) {
+  while (inst.isRunning) {
     HAL_WaitForDSData();
     GetData();
 
-    if (IsDisabled()) safetyCounter = 0;
+    if (DriverStation::IsDisabled()) {
+      safetyCounter = 0;
+    }
 
     if (++safetyCounter >= 4) {
       MotorSafety::CheckMotors();
       safetyCounter = 0;
     }
-    if (m_userInDisabled) HAL_ObserveUserProgramDisabled();
-    if (m_userInAutonomous) HAL_ObserveUserProgramAutonomous();
-    if (m_userInTeleop) HAL_ObserveUserProgramTeleop();
-    if (m_userInTest) HAL_ObserveUserProgramTest();
+    if (inst.userInDisabled) {
+      HAL_ObserveUserProgramDisabled();
+    }
+    if (inst.userInAutonomous) {
+      HAL_ObserveUserProgramAutonomous();
+    }
+    if (inst.userInTeleop) {
+      HAL_ObserveUserProgramTeleop();
+    }
+    if (inst.userInTest) {
+      HAL_ObserveUserProgramTest();
+    }
   }
 }
 
-void DriverStation::SendMatchData() {
+void SendMatchData() {
   int32_t status = 0;
   HAL_AllianceStationID alliance = HAL_GetAllianceStation(&status);
   bool isRedAlliance = false;
@@ -642,20 +776,20 @@
   HAL_MatchInfo tmpDataStore;
   HAL_GetMatchInfo(&tmpDataStore);
 
-  m_matchDataSender->alliance.SetBoolean(isRedAlliance);
-  m_matchDataSender->station.SetDouble(stationNumber);
-  m_matchDataSender->eventName.SetString(tmpDataStore.eventName);
-  m_matchDataSender->gameSpecificMessage.SetString(
+  auto& inst = GetInstance();
+  inst.matchDataSender.alliance.Set(isRedAlliance);
+  inst.matchDataSender.station.Set(stationNumber);
+  inst.matchDataSender.eventName.Set(tmpDataStore.eventName);
+  inst.matchDataSender.gameSpecificMessage.Set(
       std::string(reinterpret_cast<char*>(tmpDataStore.gameSpecificMessage),
                   tmpDataStore.gameSpecificMessageSize));
-  m_matchDataSender->matchNumber.SetDouble(tmpDataStore.matchNumber);
-  m_matchDataSender->replayNumber.SetDouble(tmpDataStore.replayNumber);
-  m_matchDataSender->matchType.SetDouble(
-      static_cast<int>(tmpDataStore.matchType));
+  inst.matchDataSender.matchNumber.Set(tmpDataStore.matchNumber);
+  inst.matchDataSender.replayNumber.Set(tmpDataStore.replayNumber);
+  inst.matchDataSender.matchType.Set(static_cast<int>(tmpDataStore.matchType));
 
   HAL_ControlWord ctlWord;
   HAL_GetControlWord(&ctlWord);
   int32_t wordInt = 0;
   std::memcpy(&wordInt, &ctlWord, sizeof(wordInt));
-  m_matchDataSender->controlWord.SetDouble(wordInt);
+  inst.matchDataSender.controlWord.Set(wordInt);
 }
diff --git a/wpilibc/src/main/native/cpp/DutyCycle.cpp b/wpilibc/src/main/native/cpp/DutyCycle.cpp
index 7e4e98d..a8375e0 100644
--- a/wpilibc/src/main/native/cpp/DutyCycle.cpp
+++ b/wpilibc/src/main/native/cpp/DutyCycle.cpp
@@ -1,46 +1,43 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/DutyCycle.h"
 
 #include <hal/DutyCycle.h>
 #include <hal/FRCUsageReporting.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
 
-#include "frc/Base.h"
 #include "frc/DigitalSource.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
 DutyCycle::DutyCycle(DigitalSource* source)
-    : m_source{source, NullDeleter<DigitalSource>()} {
-  if (m_source == nullptr) {
-    wpi_setWPIError(NullParameter);
-  } else {
-    InitDutyCycle();
+    : m_source{source, wpi::NullDeleter<DigitalSource>()} {
+  if (!m_source) {
+    throw FRC_MakeError(err::NullParameter, "{}", "source");
   }
+  InitDutyCycle();
 }
 
 DutyCycle::DutyCycle(DigitalSource& source)
-    : m_source{&source, NullDeleter<DigitalSource>()} {
+    : m_source{&source, wpi::NullDeleter<DigitalSource>()} {
   InitDutyCycle();
 }
 
 DutyCycle::DutyCycle(std::shared_ptr<DigitalSource> source)
     : m_source{std::move(source)} {
-  if (m_source == nullptr) {
-    wpi_setWPIError(NullParameter);
-  } else {
-    InitDutyCycle();
+  if (!m_source) {
+    throw FRC_MakeError(err::NullParameter, "{}", "source");
   }
+  InitDutyCycle();
 }
 
-DutyCycle::~DutyCycle() { HAL_FreeDutyCycle(m_handle); }
+DutyCycle::~DutyCycle() {
+  HAL_FreeDutyCycle(m_handle);
+}
 
 void DutyCycle::InitDutyCycle() {
   int32_t status = 0;
@@ -49,50 +46,52 @@
                               static_cast<HAL_AnalogTriggerType>(
                                   m_source->GetAnalogTriggerTypeForRouting()),
                               &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   int index = GetFPGAIndex();
   HAL_Report(HALUsageReporting::kResourceType_DutyCycle, index + 1);
-  SendableRegistry::GetInstance().AddLW(this, "Duty Cycle", index);
+  wpi::SendableRegistry::AddLW(this, "Duty Cycle", index);
 }
 
 int DutyCycle::GetFPGAIndex() const {
   int32_t status = 0;
   auto retVal = HAL_GetDutyCycleFPGAIndex(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   return retVal;
 }
 
 int DutyCycle::GetFrequency() const {
   int32_t status = 0;
   auto retVal = HAL_GetDutyCycleFrequency(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   return retVal;
 }
 
 double DutyCycle::GetOutput() const {
   int32_t status = 0;
   auto retVal = HAL_GetDutyCycleOutput(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   return retVal;
 }
 
 unsigned int DutyCycle::GetOutputRaw() const {
   int32_t status = 0;
   auto retVal = HAL_GetDutyCycleOutputRaw(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   return retVal;
 }
 
 unsigned int DutyCycle::GetOutputScaleFactor() const {
   int32_t status = 0;
   auto retVal = HAL_GetDutyCycleOutputScaleFactor(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", GetSourceChannel());
   return retVal;
 }
 
-int DutyCycle::GetSourceChannel() const { return m_source->GetChannel(); }
+int DutyCycle::GetSourceChannel() const {
+  return m_source->GetChannel();
+}
 
-void DutyCycle::InitSendable(SendableBuilder& builder) {
+void DutyCycle::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Duty Cycle");
   builder.AddDoubleProperty(
       "Frequency", [this] { return this->GetFrequency(); }, nullptr);
diff --git a/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp b/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
index 4fc5457..8e994e9 100644
--- a/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
+++ b/wpilibc/src/main/native/cpp/DutyCycleEncoder.cpp
@@ -1,19 +1,17 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/DutyCycleEncoder.h"
 
-#include "frc/Base.h"
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+
 #include "frc/Counter.h"
 #include "frc/DigitalInput.h"
 #include "frc/DigitalSource.h"
-#include "frc/DriverStation.h"
 #include "frc/DutyCycle.h"
-#include "frc/smartdashboard/SendableBuilder.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
@@ -24,12 +22,12 @@
 }
 
 DutyCycleEncoder::DutyCycleEncoder(DutyCycle& dutyCycle)
-    : m_dutyCycle{&dutyCycle, NullDeleter<DutyCycle>{}} {
+    : m_dutyCycle{&dutyCycle, wpi::NullDeleter<DutyCycle>{}} {
   Init();
 }
 
 DutyCycleEncoder::DutyCycleEncoder(DutyCycle* dutyCycle)
-    : m_dutyCycle{dutyCycle, NullDeleter<DutyCycle>{}} {
+    : m_dutyCycle{dutyCycle, wpi::NullDeleter<DutyCycle>{}} {
   Init();
 }
 
@@ -54,13 +52,16 @@
 }
 
 void DutyCycleEncoder::Init() {
-  m_simDevice = hal::SimDevice{"DutyCycleEncoder", m_dutyCycle->GetFPGAIndex()};
+  m_simDevice = hal::SimDevice{"DutyCycle:DutyCycleEncoder",
+                               m_dutyCycle->GetSourceChannel()};
 
   if (m_simDevice) {
-    m_simPosition = m_simDevice.CreateDouble("Position", false, 0.0);
-    m_simDistancePerRotation =
-        m_simDevice.CreateDouble("DistancePerRotation", false, 1.0);
-    m_simIsConnected = m_simDevice.CreateBoolean("Connected", false, true);
+    m_simPosition =
+        m_simDevice.CreateDouble("position", hal::SimDevice::kInput, 0.0);
+    m_simDistancePerRotation = m_simDevice.CreateDouble(
+        "distance_per_rot", hal::SimDevice::kOutput, 1.0);
+    m_simIsConnected =
+        m_simDevice.CreateBoolean("connected", hal::SimDevice::kInput, true);
   } else {
     m_analogTrigger = std::make_unique<AnalogTrigger>(m_dutyCycle.get());
     m_analogTrigger->SetLimitsDutyCycle(0.25, 0.75);
@@ -71,12 +72,14 @@
         m_analogTrigger->CreateOutput(AnalogTriggerType::kFallingPulse));
   }
 
-  SendableRegistry::GetInstance().AddLW(this, "DutyCycle Encoder",
-                                        m_dutyCycle->GetSourceChannel());
+  wpi::SendableRegistry::AddLW(this, "DutyCycle Encoder",
+                               m_dutyCycle->GetSourceChannel());
 }
 
 units::turn_t DutyCycleEncoder::Get() const {
-  if (m_simPosition) return units::turn_t{m_simPosition.Get()};
+  if (m_simPosition) {
+    return units::turn_t{m_simPosition.Get()};
+  }
 
   // As the values are not atomic, keep trying until we get 2 reads of the same
   // value If we don't within 10 attempts, error
@@ -92,7 +95,8 @@
     }
   }
 
-  frc::DriverStation::GetInstance().ReportWarning(
+  FRC_ReportError(
+      warn::Warning, "{}",
       "Failed to read DutyCycle Encoder. Potential Speed Overrun. Returning "
       "last value");
   return m_lastPosition;
@@ -108,7 +112,7 @@
 }
 
 double DutyCycleEncoder::GetDistance() const {
-  return Get().to<double>() * GetDistancePerRotation();
+  return Get().value() * GetDistancePerRotation();
 }
 
 int DutyCycleEncoder::GetFrequency() const {
@@ -116,12 +120,16 @@
 }
 
 void DutyCycleEncoder::Reset() {
-  if (m_counter) m_counter->Reset();
+  if (m_counter) {
+    m_counter->Reset();
+  }
   m_positionOffset = m_dutyCycle->GetOutput();
 }
 
 bool DutyCycleEncoder::IsConnected() const {
-  if (m_simIsConnected) return m_simIsConnected.Get();
+  if (m_simIsConnected) {
+    return m_simIsConnected.Get();
+  }
   return GetFrequency() > m_frequencyThreshold;
 }
 
@@ -140,7 +148,7 @@
   return m_dutyCycle->GetSourceChannel();
 }
 
-void DutyCycleEncoder::InitSendable(SendableBuilder& builder) {
+void DutyCycleEncoder::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("AbsoluteEncoder");
   builder.AddDoubleProperty(
       "Distance", [this] { return this->GetDistance(); }, nullptr);
diff --git a/wpilibc/src/main/native/cpp/Encoder.cpp b/wpilibc/src/main/native/cpp/Encoder.cpp
index 87bb45f..bef6d76 100644
--- a/wpilibc/src/main/native/cpp/Encoder.cpp
+++ b/wpilibc/src/main/native/cpp/Encoder.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Encoder.h"
 
@@ -11,12 +8,12 @@
 
 #include <hal/Encoder.h>
 #include <hal/FRCUsageReporting.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-#include "frc/Base.h"
 #include "frc/DigitalInput.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
@@ -25,196 +22,177 @@
   m_aSource = std::make_shared<DigitalInput>(aChannel);
   m_bSource = std::make_shared<DigitalInput>(bChannel);
   InitEncoder(reverseDirection, encodingType);
-  auto& registry = SendableRegistry::GetInstance();
-  registry.AddChild(this, m_aSource.get());
-  registry.AddChild(this, m_bSource.get());
+  wpi::SendableRegistry::AddChild(this, m_aSource.get());
+  wpi::SendableRegistry::AddChild(this, m_bSource.get());
 }
 
 Encoder::Encoder(DigitalSource* aSource, DigitalSource* bSource,
                  bool reverseDirection, EncodingType encodingType)
-    : m_aSource(aSource, NullDeleter<DigitalSource>()),
-      m_bSource(bSource, NullDeleter<DigitalSource>()) {
-  if (m_aSource == nullptr || m_bSource == nullptr)
-    wpi_setWPIError(NullParameter);
-  else
-    InitEncoder(reverseDirection, encodingType);
+    : m_aSource(aSource, wpi::NullDeleter<DigitalSource>()),
+      m_bSource(bSource, wpi::NullDeleter<DigitalSource>()) {
+  if (!m_aSource) {
+    throw FRC_MakeError(err::NullParameter, "{}", "aSource");
+  }
+  if (!m_bSource) {
+    throw FRC_MakeError(err::NullParameter, "{}", "bSource");
+  }
+  InitEncoder(reverseDirection, encodingType);
 }
 
 Encoder::Encoder(DigitalSource& aSource, DigitalSource& bSource,
                  bool reverseDirection, EncodingType encodingType)
-    : m_aSource(&aSource, NullDeleter<DigitalSource>()),
-      m_bSource(&bSource, NullDeleter<DigitalSource>()) {
+    : m_aSource(&aSource, wpi::NullDeleter<DigitalSource>()),
+      m_bSource(&bSource, wpi::NullDeleter<DigitalSource>()) {
   InitEncoder(reverseDirection, encodingType);
 }
 
 Encoder::Encoder(std::shared_ptr<DigitalSource> aSource,
                  std::shared_ptr<DigitalSource> bSource, bool reverseDirection,
                  EncodingType encodingType)
-    : m_aSource(aSource), m_bSource(bSource) {
-  if (m_aSource == nullptr || m_bSource == nullptr)
-    wpi_setWPIError(NullParameter);
-  else
-    InitEncoder(reverseDirection, encodingType);
+    : m_aSource(std::move(aSource)), m_bSource(std::move(bSource)) {
+  if (!m_aSource) {
+    throw FRC_MakeError(err::NullParameter, "{}", "aSource");
+  }
+  if (!m_bSource) {
+    throw FRC_MakeError(err::NullParameter, "{}", "bSource");
+  }
+  InitEncoder(reverseDirection, encodingType);
 }
 
 Encoder::~Encoder() {
   int32_t status = 0;
   HAL_FreeEncoder(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_ReportError(status, "{}", "FreeEncoder");
 }
 
 int Encoder::Get() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int value = HAL_GetEncoder(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Get");
   return value;
 }
 
 void Encoder::Reset() {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_ResetEncoder(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Reset");
 }
 
-double Encoder::GetPeriod() const {
-  if (StatusIsFatal()) return 0.0;
+units::second_t Encoder::GetPeriod() const {
   int32_t status = 0;
   double value = HAL_GetEncoderPeriod(m_encoder, &status);
-  wpi_setHALError(status);
-  return value;
+  FRC_CheckErrorStatus(status, "{}", "GetPeriod");
+  return units::second_t{value};
 }
 
-void Encoder::SetMaxPeriod(double maxPeriod) {
-  if (StatusIsFatal()) return;
+void Encoder::SetMaxPeriod(units::second_t maxPeriod) {
   int32_t status = 0;
-  HAL_SetEncoderMaxPeriod(m_encoder, maxPeriod, &status);
-  wpi_setHALError(status);
+  HAL_SetEncoderMaxPeriod(m_encoder, maxPeriod.value(), &status);
+  FRC_CheckErrorStatus(status, "{}", "SetMaxPeriod");
 }
 
 bool Encoder::GetStopped() const {
-  if (StatusIsFatal()) return true;
   int32_t status = 0;
   bool value = HAL_GetEncoderStopped(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetStopped");
   return value;
 }
 
 bool Encoder::GetDirection() const {
-  if (StatusIsFatal()) return false;
   int32_t status = 0;
   bool value = HAL_GetEncoderDirection(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetDirection");
   return value;
 }
 
 int Encoder::GetRaw() const {
-  if (StatusIsFatal()) return 0;
   int32_t status = 0;
   int value = HAL_GetEncoderRaw(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetRaw");
   return value;
 }
 
 int Encoder::GetEncodingScale() const {
   int32_t status = 0;
   int val = HAL_GetEncoderEncodingScale(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetEncodingScale");
   return val;
 }
 
 double Encoder::GetDistance() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double value = HAL_GetEncoderDistance(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetDistance");
   return value;
 }
 
 double Encoder::GetRate() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double value = HAL_GetEncoderRate(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetRate");
   return value;
 }
 
 void Encoder::SetMinRate(double minRate) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetEncoderMinRate(m_encoder, minRate, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetMinRate");
 }
 
 void Encoder::SetDistancePerPulse(double distancePerPulse) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetEncoderDistancePerPulse(m_encoder, distancePerPulse, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetDistancePerPulse");
 }
 
 double Encoder::GetDistancePerPulse() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double distancePerPulse = HAL_GetEncoderDistancePerPulse(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetDistancePerPulse");
   return distancePerPulse;
 }
 
 void Encoder::SetReverseDirection(bool reverseDirection) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetEncoderReverseDirection(m_encoder, reverseDirection, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetReverseDirection");
 }
 
 void Encoder::SetSamplesToAverage(int samplesToAverage) {
   if (samplesToAverage < 1 || samplesToAverage > 127) {
-    wpi_setWPIErrorWithContext(
-        ParameterOutOfRange,
-        "Average counter values must be between 1 and 127");
-    return;
+    throw FRC_MakeError(
+        err::ParameterOutOfRange,
+        "Average counter values must be between 1 and 127, got {}",
+        samplesToAverage);
   }
   int32_t status = 0;
   HAL_SetEncoderSamplesToAverage(m_encoder, samplesToAverage, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetSamplesToAverage");
 }
 
 int Encoder::GetSamplesToAverage() const {
   int32_t status = 0;
   int result = HAL_GetEncoderSamplesToAverage(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetSamplesToAverage");
   return result;
 }
 
-double Encoder::PIDGet() {
-  if (StatusIsFatal()) return 0.0;
-  switch (GetPIDSourceType()) {
-    case PIDSourceType::kDisplacement:
-      return GetDistance();
-    case PIDSourceType::kRate:
-      return GetRate();
-    default:
-      return 0.0;
-  }
-}
-
 void Encoder::SetIndexSource(int channel, Encoder::IndexingType type) {
   // Force digital input if just given an index
   m_indexSource = std::make_shared<DigitalInput>(channel);
-  SendableRegistry::GetInstance().AddChild(this, m_indexSource.get());
+  wpi::SendableRegistry::AddChild(this, m_indexSource.get());
   SetIndexSource(*m_indexSource.get(), type);
 }
 
 void Encoder::SetIndexSource(const DigitalSource& source,
                              Encoder::IndexingType type) {
   int32_t status = 0;
-  HAL_SetEncoderIndexSource(
-      m_encoder, source.GetPortHandleForRouting(),
-      (HAL_AnalogTriggerType)source.GetAnalogTriggerTypeForRouting(),
-      (HAL_EncoderIndexingType)type, &status);
-  wpi_setHALError(status);
+  HAL_SetEncoderIndexSource(m_encoder, source.GetPortHandleForRouting(),
+                            static_cast<HAL_AnalogTriggerType>(
+                                source.GetAnalogTriggerTypeForRouting()),
+                            static_cast<HAL_EncoderIndexingType>(type),
+                            &status);
+  FRC_CheckErrorStatus(status, "{}", "SetIndexSource");
 }
 
 void Encoder::SetSimDevice(HAL_SimDeviceHandle device) {
@@ -224,47 +202,49 @@
 int Encoder::GetFPGAIndex() const {
   int32_t status = 0;
   int val = HAL_GetEncoderFPGAIndex(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetFPGAIndex");
   return val;
 }
 
-void Encoder::InitSendable(SendableBuilder& builder) {
+void Encoder::InitSendable(wpi::SendableBuilder& builder) {
   int32_t status = 0;
   HAL_EncoderEncodingType type = HAL_GetEncoderEncodingType(m_encoder, &status);
-  wpi_setHALError(status);
-  if (type == HAL_EncoderEncodingType::HAL_Encoder_k4X)
+  FRC_CheckErrorStatus(status, "{}", "GetEncodingType");
+  if (type == HAL_EncoderEncodingType::HAL_Encoder_k4X) {
     builder.SetSmartDashboardType("Quadrature Encoder");
-  else
+  } else {
     builder.SetSmartDashboardType("Encoder");
+  }
 
   builder.AddDoubleProperty(
-      "Speed", [=]() { return GetRate(); }, nullptr);
+      "Speed", [=] { return GetRate(); }, nullptr);
   builder.AddDoubleProperty(
-      "Distance", [=]() { return GetDistance(); }, nullptr);
+      "Distance", [=] { return GetDistance(); }, nullptr);
   builder.AddDoubleProperty(
-      "Distance per Tick", [=]() { return GetDistancePerPulse(); }, nullptr);
+      "Distance per Tick", [=] { return GetDistancePerPulse(); }, nullptr);
 }
 
 void Encoder::InitEncoder(bool reverseDirection, EncodingType encodingType) {
   int32_t status = 0;
   m_encoder = HAL_InitializeEncoder(
       m_aSource->GetPortHandleForRouting(),
-      (HAL_AnalogTriggerType)m_aSource->GetAnalogTriggerTypeForRouting(),
+      static_cast<HAL_AnalogTriggerType>(
+          m_aSource->GetAnalogTriggerTypeForRouting()),
       m_bSource->GetPortHandleForRouting(),
-      (HAL_AnalogTriggerType)m_bSource->GetAnalogTriggerTypeForRouting(),
-      reverseDirection, (HAL_EncoderEncodingType)encodingType, &status);
-  wpi_setHALError(status);
+      static_cast<HAL_AnalogTriggerType>(
+          m_bSource->GetAnalogTriggerTypeForRouting()),
+      reverseDirection, static_cast<HAL_EncoderEncodingType>(encodingType),
+      &status);
+  FRC_CheckErrorStatus(status, "{}", "InitEncoder");
 
   HAL_Report(HALUsageReporting::kResourceType_Encoder, GetFPGAIndex() + 1,
              encodingType);
-  SendableRegistry::GetInstance().AddLW(this, "Encoder",
-                                        m_aSource->GetChannel());
+  wpi::SendableRegistry::AddLW(this, "Encoder", m_aSource->GetChannel());
 }
 
 double Encoder::DecodingScaleFactor() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double val = HAL_GetEncoderDecodingScaleFactor(m_encoder, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "DecodingScaleFactor");
   return val;
 }
diff --git a/wpilibc/src/main/native/cpp/Error.cpp b/wpilibc/src/main/native/cpp/Error.cpp
deleted file mode 100644
index 5e072c9..0000000
--- a/wpilibc/src/main/native/cpp/Error.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/Error.h"
-
-#include <wpi/Path.h>
-#include <wpi/StackTrace.h>
-
-#include "frc/Base.h"
-#include "frc/DriverStation.h"
-#include "frc/Timer.h"
-
-using namespace frc;
-
-Error::Error(Code code, const wpi::Twine& contextMessage,
-             wpi::StringRef filename, wpi::StringRef function, int lineNumber,
-             const ErrorBase* originatingObject) {
-  Set(code, contextMessage, filename, function, lineNumber, originatingObject);
-}
-
-bool Error::operator<(const Error& rhs) const {
-  if (m_code < rhs.m_code) {
-    return true;
-  } else if (m_message < rhs.m_message) {
-    return true;
-  } else if (m_filename < rhs.m_filename) {
-    return true;
-  } else if (m_function < rhs.m_function) {
-    return true;
-  } else if (m_lineNumber < rhs.m_lineNumber) {
-    return true;
-  } else if (m_originatingObject < rhs.m_originatingObject) {
-    return true;
-  } else if (m_timestamp < rhs.m_timestamp) {
-    return true;
-  } else {
-    return false;
-  }
-}
-
-Error::Code Error::GetCode() const { return m_code; }
-
-std::string Error::GetMessage() const { return m_message; }
-
-std::string Error::GetFilename() const { return m_filename; }
-
-std::string Error::GetFunction() const { return m_function; }
-
-int Error::GetLineNumber() const { return m_lineNumber; }
-
-const ErrorBase* Error::GetOriginatingObject() const {
-  return m_originatingObject;
-}
-
-double Error::GetTimestamp() const { return m_timestamp; }
-
-void Error::Set(Code code, const wpi::Twine& contextMessage,
-                wpi::StringRef filename, wpi::StringRef function,
-                int lineNumber, const ErrorBase* originatingObject) {
-  bool report = true;
-
-  if (code == m_code && GetTime() - m_timestamp < 1) {
-    report = false;
-  }
-
-  m_code = code;
-  m_message = contextMessage.str();
-  m_filename = filename;
-  m_function = function;
-  m_lineNumber = lineNumber;
-  m_originatingObject = originatingObject;
-
-  if (report) {
-    m_timestamp = GetTime();
-    Report();
-  }
-}
-
-void Error::Report() {
-  DriverStation::ReportError(
-      true, m_code, m_message,
-      m_function + wpi::Twine(" [") + wpi::sys::path::filename(m_filename) +
-          wpi::Twine(':') + wpi::Twine(m_lineNumber) + wpi::Twine(']'),
-      wpi::GetStackTrace(4));
-}
-
-void Error::Clear() {
-  m_code = 0;
-  m_message = "";
-  m_filename = "";
-  m_function = "";
-  m_lineNumber = 0;
-  m_originatingObject = nullptr;
-  m_timestamp = 0.0;
-}
diff --git a/wpilibc/src/main/native/cpp/ErrorBase.cpp b/wpilibc/src/main/native/cpp/ErrorBase.cpp
deleted file mode 100644
index 8c7c5a2..0000000
--- a/wpilibc/src/main/native/cpp/ErrorBase.cpp
+++ /dev/null
@@ -1,187 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/ErrorBase.h"
-
-#include <cerrno>
-#include <cstring>
-#include <set>
-#include <utility>
-
-#include <hal/HALBase.h>
-#include <wpi/Format.h>
-#include <wpi/SmallString.h>
-#include <wpi/mutex.h>
-#include <wpi/raw_ostream.h>
-
-#include "frc/Base.h"
-
-using namespace frc;
-
-namespace {
-struct GlobalErrors {
-  wpi::mutex mutex;
-  std::set<Error> errors;
-  const Error* lastError{nullptr};
-
-  static GlobalErrors& GetInstance();
-  static void Insert(const Error& error);
-  static void Insert(Error&& error);
-};
-}  // namespace
-
-GlobalErrors& GlobalErrors::GetInstance() {
-  static GlobalErrors inst;
-  return inst;
-}
-
-void GlobalErrors::Insert(const Error& error) {
-  GlobalErrors& inst = GetInstance();
-  std::scoped_lock lock(inst.mutex);
-  inst.lastError = &(*inst.errors.insert(error).first);
-}
-
-void GlobalErrors::Insert(Error&& error) {
-  GlobalErrors& inst = GetInstance();
-  std::scoped_lock lock(inst.mutex);
-  inst.lastError = &(*inst.errors.insert(std::move(error)).first);
-}
-
-ErrorBase::ErrorBase() { HAL_Initialize(500, 0); }
-
-Error& ErrorBase::GetError() { return m_error; }
-
-const Error& ErrorBase::GetError() const { return m_error; }
-
-void ErrorBase::ClearError() const { m_error.Clear(); }
-
-void ErrorBase::SetErrnoError(const wpi::Twine& contextMessage,
-                              wpi::StringRef filename, wpi::StringRef function,
-                              int lineNumber) const {
-  wpi::SmallString<128> buf;
-  wpi::raw_svector_ostream err(buf);
-  int errNo = errno;
-  if (errNo == 0) {
-    err << "OK: ";
-  } else {
-    err << std::strerror(errNo) << " (" << wpi::format_hex(errNo, 10, true)
-        << "): ";
-  }
-
-  // Set the current error information for this object.
-  m_error.Set(-1, err.str() + contextMessage, filename, function, lineNumber,
-              this);
-
-  // Update the global error if there is not one already set.
-  GlobalErrors::Insert(m_error);
-}
-
-void ErrorBase::SetImaqError(int success, const wpi::Twine& contextMessage,
-                             wpi::StringRef filename, wpi::StringRef function,
-                             int lineNumber) const {
-  // If there was an error
-  if (success <= 0) {
-    // Set the current error information for this object.
-    m_error.Set(success, wpi::Twine(success) + ": " + contextMessage, filename,
-                function, lineNumber, this);
-
-    // Update the global error if there is not one already set.
-    GlobalErrors::Insert(m_error);
-  }
-}
-
-void ErrorBase::SetError(Error::Code code, const wpi::Twine& contextMessage,
-                         wpi::StringRef filename, wpi::StringRef function,
-                         int lineNumber) const {
-  //  If there was an error
-  if (code != 0) {
-    //  Set the current error information for this object.
-    m_error.Set(code, contextMessage, filename, function, lineNumber, this);
-
-    // Update the global error if there is not one already set.
-    GlobalErrors::Insert(m_error);
-  }
-}
-
-void ErrorBase::SetErrorRange(Error::Code code, int32_t minRange,
-                              int32_t maxRange, int32_t requestedValue,
-                              const wpi::Twine& contextMessage,
-                              wpi::StringRef filename, wpi::StringRef function,
-                              int lineNumber) const {
-  //  If there was an error
-  if (code != 0) {
-    //  Set the current error information for this object.
-    m_error.Set(code,
-                contextMessage + ", Minimum Value: " + wpi::Twine(minRange) +
-                    ", MaximumValue: " + wpi::Twine(maxRange) +
-                    ", Requested Value: " + wpi::Twine(requestedValue),
-                filename, function, lineNumber, this);
-
-    // Update the global error if there is not one already set.
-    GlobalErrors::Insert(m_error);
-  }
-}
-
-void ErrorBase::SetWPIError(const wpi::Twine& errorMessage, Error::Code code,
-                            const wpi::Twine& contextMessage,
-                            wpi::StringRef filename, wpi::StringRef function,
-                            int lineNumber) const {
-  //  Set the current error information for this object.
-  m_error.Set(code, errorMessage + ": " + contextMessage, filename, function,
-              lineNumber, this);
-
-  // Update the global error if there is not one already set.
-  GlobalErrors::Insert(m_error);
-}
-
-void ErrorBase::CloneError(const ErrorBase& rhs) const {
-  m_error = rhs.GetError();
-}
-
-bool ErrorBase::StatusIsFatal() const { return m_error.GetCode() < 0; }
-
-void ErrorBase::SetGlobalError(Error::Code code,
-                               const wpi::Twine& contextMessage,
-                               wpi::StringRef filename, wpi::StringRef function,
-                               int lineNumber) {
-  // If there was an error
-  if (code != 0) {
-    // Set the current error information for this object.
-    GlobalErrors::Insert(
-        Error(code, contextMessage, filename, function, lineNumber, nullptr));
-  }
-}
-
-void ErrorBase::SetGlobalWPIError(const wpi::Twine& errorMessage,
-                                  const wpi::Twine& contextMessage,
-                                  wpi::StringRef filename,
-                                  wpi::StringRef function, int lineNumber) {
-  GlobalErrors::Insert(Error(-1, errorMessage + ": " + contextMessage, filename,
-                             function, lineNumber, nullptr));
-}
-
-Error ErrorBase::GetGlobalError() {
-  auto& inst = GlobalErrors::GetInstance();
-  std::scoped_lock mutex(inst.mutex);
-  if (!inst.lastError) return Error{};
-  return *inst.lastError;
-}
-
-std::vector<Error> ErrorBase::GetGlobalErrors() {
-  auto& inst = GlobalErrors::GetInstance();
-  std::scoped_lock mutex(inst.mutex);
-  std::vector<Error> rv;
-  for (auto&& error : inst.errors) rv.push_back(error);
-  return rv;
-}
-
-void ErrorBase::ClearGlobalErrors() {
-  auto& inst = GlobalErrors::GetInstance();
-  std::scoped_lock mutex(inst.mutex);
-  inst.errors.clear();
-  inst.lastError = nullptr;
-}
diff --git a/wpilibc/src/main/native/cpp/Errors.cpp b/wpilibc/src/main/native/cpp/Errors.cpp
new file mode 100644
index 0000000..0ff4dc1
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/Errors.cpp
@@ -0,0 +1,81 @@
+// 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.
+
+#include "frc/Errors.h"
+
+#include <exception>
+
+#include <hal/DriverStation.h>
+#include <hal/HALBase.h>
+#include <wpi/StackTrace.h>
+#include <wpi/fs.h>
+
+using namespace frc;
+
+RuntimeError::RuntimeError(int32_t code, std::string&& loc, std::string&& stack,
+                           std::string&& message)
+    : runtime_error{std::move(message)}, m_data{std::make_shared<Data>()} {
+  m_data->code = code;
+  m_data->loc = std::move(loc);
+  m_data->stack = stack;
+}
+
+RuntimeError::RuntimeError(int32_t code, const char* fileName, int lineNumber,
+                           const char* funcName, std::string&& stack,
+                           std::string&& message)
+    : RuntimeError{
+          code,
+          fmt::format("{} [{}:{}]", funcName,
+                      fs::path{fileName}.filename().string(), lineNumber),
+          std::move(stack), std::move(message)} {}
+
+void RuntimeError::Report() const {
+  HAL_SendError(m_data->code < 0, m_data->code, 0, what(), m_data->loc.c_str(),
+                m_data->stack.c_str(), 1);
+}
+
+const char* frc::GetErrorMessage(int32_t* code) {
+  switch (*code) {
+#define S(label, offset, message) \
+  case err::label:                \
+    return message;
+#include "frc/WPIErrors.mac"
+#undef S
+#define S(label, offset, message) \
+  case warn::label:               \
+    return message;
+#include "frc/WPIWarnings.mac"
+#undef S
+    default:
+      return HAL_GetLastError(code);
+  }
+}
+
+void frc::ReportErrorV(int32_t status, const char* fileName, int lineNumber,
+                       const char* funcName, fmt::string_view format,
+                       fmt::format_args args) {
+  if (status == 0) {
+    return;
+  }
+  fmt::memory_buffer out;
+  fmt::format_to(fmt::appender{out}, "{}: ", GetErrorMessage(&status));
+  fmt::vformat_to(fmt::appender{out}, format, args);
+  out.push_back('\0');
+  HAL_SendError(status < 0, status, 0, out.data(), funcName,
+                wpi::GetStackTrace(2).c_str(), 1);
+}
+
+RuntimeError frc::MakeErrorV(int32_t status, const char* fileName,
+                             int lineNumber, const char* funcName,
+                             fmt::string_view format, fmt::format_args args) {
+  fmt::memory_buffer out;
+  fmt::format_to(fmt::appender{out}, "{}: ", GetErrorMessage(&status));
+  fmt::vformat_to(fmt::appender{out}, format, args);
+  return RuntimeError{status,
+                      fileName,
+                      lineNumber,
+                      funcName,
+                      wpi::GetStackTrace(2),
+                      fmt::to_string(out)};
+}
diff --git a/wpilibc/src/main/native/cpp/Filesystem.cpp b/wpilibc/src/main/native/cpp/Filesystem.cpp
index 7d2ea1d..d497779 100644
--- a/wpilibc/src/main/native/cpp/Filesystem.cpp
+++ b/wpilibc/src/main/native/cpp/Filesystem.cpp
@@ -1,37 +1,29 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Filesystem.h"
 
-#include <wpi/FileSystem.h>
-#include <wpi/Path.h>
+#include <wpi/fs.h>
 
 #include "frc/RobotBase.h"
 
-void frc::filesystem::GetLaunchDirectory(wpi::SmallVectorImpl<char>& result) {
-  wpi::sys::fs::current_path(result);
+std::string frc::filesystem::GetLaunchDirectory() {
+  return fs::current_path().string();
 }
 
-void frc::filesystem::GetOperatingDirectory(
-    wpi::SmallVectorImpl<char>& result) {
+std::string frc::filesystem::GetOperatingDirectory() {
   if constexpr (RobotBase::IsReal()) {
-    wpi::sys::path::native("/home/lvuser", result);
+    return "/home/lvuser";
   } else {
-    frc::filesystem::GetLaunchDirectory(result);
+    return frc::filesystem::GetLaunchDirectory();
   }
 }
 
-void frc::filesystem::GetDeployDirectory(wpi::SmallVectorImpl<char>& result) {
-  frc::filesystem::GetOperatingDirectory(result);
+std::string frc::filesystem::GetDeployDirectory() {
   if constexpr (RobotBase::IsReal()) {
-    wpi::sys::path::append(result, "deploy");
+    return "/home/lvuser/deploy";
   } else {
-    wpi::sys::path::append(result, "src");
-    wpi::sys::path::append(result, "main");
-    wpi::sys::path::append(result, "deploy");
+    return (fs::current_path() / "src" / "main" / "deploy").string();
   }
 }
diff --git a/wpilibc/src/main/native/cpp/GearTooth.cpp b/wpilibc/src/main/native/cpp/GearTooth.cpp
deleted file mode 100644
index 5fb5021..0000000
--- a/wpilibc/src/main/native/cpp/GearTooth.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/GearTooth.h"
-
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-constexpr double GearTooth::kGearToothThreshold;
-
-GearTooth::GearTooth(int channel, bool directionSensitive) : Counter(channel) {
-  EnableDirectionSensing(directionSensitive);
-  SendableRegistry::GetInstance().SetName(this, "GearTooth", channel);
-}
-
-GearTooth::GearTooth(DigitalSource* source, bool directionSensitive)
-    : Counter(source) {
-  EnableDirectionSensing(directionSensitive);
-  SendableRegistry::GetInstance().SetName(this, "GearTooth",
-                                          source->GetChannel());
-}
-
-GearTooth::GearTooth(std::shared_ptr<DigitalSource> source,
-                     bool directionSensitive)
-    : Counter(source) {
-  EnableDirectionSensing(directionSensitive);
-  SendableRegistry::GetInstance().SetName(this, "GearTooth",
-                                          source->GetChannel());
-}
-
-void GearTooth::EnableDirectionSensing(bool directionSensitive) {
-  if (directionSensitive) {
-    SetPulseLengthMode(kGearToothThreshold);
-  }
-}
-
-void GearTooth::InitSendable(SendableBuilder& builder) {
-  Counter::InitSendable(builder);
-  builder.SetSmartDashboardType("Gear Tooth");
-}
diff --git a/wpilibc/src/main/native/cpp/GenericHID.cpp b/wpilibc/src/main/native/cpp/GenericHID.cpp
index 4a264c8..6c186eb 100644
--- a/wpilibc/src/main/native/cpp/GenericHID.cpp
+++ b/wpilibc/src/main/native/cpp/GenericHID.cpp
@@ -1,69 +1,74 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/GenericHID.h"
 
 #include <hal/DriverStation.h>
 
 #include "frc/DriverStation.h"
-#include "frc/WPIErrors.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
-GenericHID::GenericHID(int port) : m_ds(&DriverStation::GetInstance()) {
-  if (port >= DriverStation::kJoystickPorts) {
-    wpi_setWPIError(BadJoystickIndex);
+GenericHID::GenericHID(int port) {
+  if (port < 0 || port >= DriverStation::kJoystickPorts) {
+    throw FRC_MakeError(warn::BadJoystickIndex, "port {} out of range", port);
   }
   m_port = port;
 }
 
 bool GenericHID::GetRawButton(int button) const {
-  return m_ds->GetStickButton(m_port, button);
+  return DriverStation::GetStickButton(m_port, button);
 }
 
 bool GenericHID::GetRawButtonPressed(int button) {
-  return m_ds->GetStickButtonPressed(m_port, button);
+  return DriverStation::GetStickButtonPressed(m_port, button);
 }
 
 bool GenericHID::GetRawButtonReleased(int button) {
-  return m_ds->GetStickButtonReleased(m_port, button);
+  return DriverStation::GetStickButtonReleased(m_port, button);
 }
 
 double GenericHID::GetRawAxis(int axis) const {
-  return m_ds->GetStickAxis(m_port, axis);
+  return DriverStation::GetStickAxis(m_port, axis);
 }
 
-int GenericHID::GetPOV(int pov) const { return m_ds->GetStickPOV(m_port, pov); }
+int GenericHID::GetPOV(int pov) const {
+  return DriverStation::GetStickPOV(m_port, pov);
+}
 
-int GenericHID::GetAxisCount() const { return m_ds->GetStickAxisCount(m_port); }
+int GenericHID::GetAxisCount() const {
+  return DriverStation::GetStickAxisCount(m_port);
+}
 
-int GenericHID::GetPOVCount() const { return m_ds->GetStickPOVCount(m_port); }
+int GenericHID::GetPOVCount() const {
+  return DriverStation::GetStickPOVCount(m_port);
+}
 
 int GenericHID::GetButtonCount() const {
-  return m_ds->GetStickButtonCount(m_port);
+  return DriverStation::GetStickButtonCount(m_port);
 }
 
 bool GenericHID::IsConnected() const {
-  return m_ds->IsJoystickConnected(m_port);
+  return DriverStation::IsJoystickConnected(m_port);
 }
 
 GenericHID::HIDType GenericHID::GetType() const {
-  return static_cast<HIDType>(m_ds->GetJoystickType(m_port));
+  return static_cast<HIDType>(DriverStation::GetJoystickType(m_port));
 }
 
 std::string GenericHID::GetName() const {
-  return m_ds->GetJoystickName(m_port);
+  return DriverStation::GetJoystickName(m_port);
 }
 
 int GenericHID::GetAxisType(int axis) const {
-  return m_ds->GetJoystickAxisType(m_port, axis);
+  return DriverStation::GetJoystickAxisType(m_port, axis);
 }
 
-int GenericHID::GetPort() const { return m_port; }
+int GenericHID::GetPort() const {
+  return m_port;
+}
 
 void GenericHID::SetOutput(int outputNumber, bool value) {
   m_outputs =
@@ -78,10 +83,11 @@
 }
 
 void GenericHID::SetRumble(RumbleType type, double value) {
-  if (value < 0)
+  if (value < 0) {
     value = 0;
-  else if (value > 1)
+  } else if (value > 1) {
     value = 1;
+  }
   if (type == kLeftRumble) {
     m_leftRumble = value * 65535;
   } else {
diff --git a/wpilibc/src/main/native/cpp/GyroBase.cpp b/wpilibc/src/main/native/cpp/GyroBase.cpp
deleted file mode 100644
index ba9b2f0..0000000
--- a/wpilibc/src/main/native/cpp/GyroBase.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/GyroBase.h"
-
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-
-using namespace frc;
-
-double GyroBase::PIDGet() {
-  switch (GetPIDSourceType()) {
-    case PIDSourceType::kRate:
-      return GetRate();
-    case PIDSourceType::kDisplacement:
-      return GetAngle();
-    default:
-      return 0;
-  }
-}
-
-void GyroBase::InitSendable(SendableBuilder& builder) {
-  builder.SetSmartDashboardType("Gyro");
-  builder.AddDoubleProperty(
-      "Value", [=]() { return GetAngle(); }, nullptr);
-}
diff --git a/wpilibc/src/main/native/cpp/I2C.cpp b/wpilibc/src/main/native/cpp/I2C.cpp
index 21d92f2..adf1c54 100644
--- a/wpilibc/src/main/native/cpp/I2C.cpp
+++ b/wpilibc/src/main/native/cpp/I2C.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/I2C.h"
 
@@ -12,7 +9,7 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/I2C.h>
 
-#include "frc/WPIErrors.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
@@ -20,23 +17,34 @@
     : m_port(static_cast<HAL_I2CPort>(port)), m_deviceAddress(deviceAddress) {
   int32_t status = 0;
   HAL_InitializeI2C(m_port, &status);
-  // wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", port);
 
   HAL_Report(HALUsageReporting::kResourceType_I2C, deviceAddress);
 }
 
-I2C::~I2C() { HAL_CloseI2C(m_port); }
+I2C::~I2C() {
+  HAL_CloseI2C(m_port);
+}
+
+I2C::Port I2C::GetPort() const {
+  return static_cast<Port>(static_cast<int>(m_port));
+}
+
+int I2C::GetDeviceAddress() const {
+  return m_deviceAddress;
+}
 
 bool I2C::Transaction(uint8_t* dataToSend, int sendSize, uint8_t* dataReceived,
                       int receiveSize) {
   int32_t status = 0;
   status = HAL_TransactionI2C(m_port, m_deviceAddress, dataToSend, sendSize,
                               dataReceived, receiveSize);
-  // wpi_setHALError(status);
   return status < 0;
 }
 
-bool I2C::AddressOnly() { return Transaction(nullptr, 0, nullptr, 0); }
+bool I2C::AddressOnly() {
+  return Transaction(nullptr, 0, nullptr, 0);
+}
 
 bool I2C::Write(int registerAddress, uint8_t data) {
   uint8_t buffer[2];
@@ -55,12 +63,10 @@
 
 bool I2C::Read(int registerAddress, int count, uint8_t* buffer) {
   if (count < 1) {
-    wpi_setWPIErrorWithContext(ParameterOutOfRange, "count");
-    return true;
+    throw FRC_MakeError(err::ParameterOutOfRange, "count {}", count);
   }
-  if (buffer == nullptr) {
-    wpi_setWPIErrorWithContext(NullParameter, "buffer");
-    return true;
+  if (!buffer) {
+    throw FRC_MakeError(err::NullParameter, "{}", "buffer");
   }
   uint8_t regAddr = registerAddress;
   return Transaction(&regAddr, sizeof(regAddr), buffer, count);
@@ -68,12 +74,10 @@
 
 bool I2C::ReadOnly(int count, uint8_t* buffer) {
   if (count < 1) {
-    wpi_setWPIErrorWithContext(ParameterOutOfRange, "count");
-    return true;
+    throw FRC_MakeError(err::ParameterOutOfRange, "count {}", count);
   }
-  if (buffer == nullptr) {
-    wpi_setWPIErrorWithContext(NullParameter, "buffer");
-    return true;
+  if (!buffer) {
+    throw FRC_MakeError(err::NullParameter, "{}", "buffer");
   }
   return HAL_ReadI2C(m_port, m_deviceAddress, buffer, count) < 0;
 }
@@ -86,10 +90,14 @@
        i += 4, curRegisterAddress += 4) {
     int toRead = count - i < 4 ? count - i : 4;
     // Read the chunk of data.  Return false if the sensor does not respond.
-    if (Read(curRegisterAddress, toRead, deviceData)) return false;
+    if (Read(curRegisterAddress, toRead, deviceData)) {
+      return false;
+    }
 
     for (int j = 0; j < toRead; j++) {
-      if (deviceData[j] != expected[i + j]) return false;
+      if (deviceData[j] != expected[i + j]) {
+        return false;
+      }
     }
   }
   return true;
diff --git a/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp b/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp
deleted file mode 100644
index 744e349..0000000
--- a/wpilibc/src/main/native/cpp/InterruptableSensorBase.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/InterruptableSensorBase.h"
-
-#include "frc/Utility.h"
-#include "frc/WPIErrors.h"
-
-using namespace frc;
-
-InterruptableSensorBase::~InterruptableSensorBase() {
-  if (m_interrupt == HAL_kInvalidHandle) return;
-  int32_t status = 0;
-  HAL_CleanInterrupts(m_interrupt, &status);
-  // Ignore status, as an invalid handle just needs to be ignored.
-}
-
-void InterruptableSensorBase::RequestInterrupts(
-    HAL_InterruptHandlerFunction handler, void* param) {
-  if (StatusIsFatal()) return;
-
-  wpi_assert(m_interrupt == HAL_kInvalidHandle);
-  AllocateInterrupts(false);
-  if (StatusIsFatal()) return;  // if allocate failed, out of interrupts
-
-  int32_t status = 0;
-  HAL_RequestInterrupts(
-      m_interrupt, GetPortHandleForRouting(),
-      static_cast<HAL_AnalogTriggerType>(GetAnalogTriggerTypeForRouting()),
-      &status);
-  SetUpSourceEdge(true, false);
-  HAL_AttachInterruptHandler(m_interrupt, handler, param, &status);
-  wpi_setHALError(status);
-}
-
-void InterruptableSensorBase::RequestInterrupts(InterruptEventHandler handler) {
-  if (StatusIsFatal()) return;
-
-  wpi_assert(m_interrupt == HAL_kInvalidHandle);
-  AllocateInterrupts(false);
-  if (StatusIsFatal()) return;  // if allocate failed, out of interrupts
-
-  m_interruptHandler =
-      std::make_unique<InterruptEventHandler>(std::move(handler));
-
-  int32_t status = 0;
-  HAL_RequestInterrupts(
-      m_interrupt, GetPortHandleForRouting(),
-      static_cast<HAL_AnalogTriggerType>(GetAnalogTriggerTypeForRouting()),
-      &status);
-  SetUpSourceEdge(true, false);
-  HAL_AttachInterruptHandler(
-      m_interrupt,
-      [](uint32_t mask, void* param) {
-        auto self = reinterpret_cast<InterruptEventHandler*>(param);
-        // Rising edge result is the interrupt bit set in the byte 0xFF
-        // Falling edge result is the interrupt bit set in the byte 0xFF00
-        // Set any bit set to be true for that edge, and AND the 2 results
-        // together to match the existing enum for all interrupts
-        int32_t rising = (mask & 0xFF) ? 0x1 : 0x0;
-        int32_t falling = ((mask & 0xFF00) ? 0x0100 : 0x0);
-        WaitResult res = static_cast<WaitResult>(falling | rising);
-        (*self)(res);
-      },
-      m_interruptHandler.get(), &status);
-  wpi_setHALError(status);
-}
-
-void InterruptableSensorBase::RequestInterrupts() {
-  if (StatusIsFatal()) return;
-
-  wpi_assert(m_interrupt == HAL_kInvalidHandle);
-  AllocateInterrupts(true);
-  if (StatusIsFatal()) return;  // if allocate failed, out of interrupts
-
-  int32_t status = 0;
-  HAL_RequestInterrupts(
-      m_interrupt, GetPortHandleForRouting(),
-      static_cast<HAL_AnalogTriggerType>(GetAnalogTriggerTypeForRouting()),
-      &status);
-  wpi_setHALError(status);
-  SetUpSourceEdge(true, false);
-}
-
-void InterruptableSensorBase::CancelInterrupts() {
-  if (StatusIsFatal()) return;
-  wpi_assert(m_interrupt != HAL_kInvalidHandle);
-  int32_t status = 0;
-  HAL_CleanInterrupts(m_interrupt, &status);
-  // Ignore status, as an invalid handle just needs to be ignored.
-  m_interrupt = HAL_kInvalidHandle;
-  m_interruptHandler = nullptr;
-}
-
-InterruptableSensorBase::WaitResult InterruptableSensorBase::WaitForInterrupt(
-    double timeout, bool ignorePrevious) {
-  if (StatusIsFatal()) return InterruptableSensorBase::kTimeout;
-  wpi_assert(m_interrupt != HAL_kInvalidHandle);
-  int32_t status = 0;
-  int result;
-
-  result = HAL_WaitForInterrupt(m_interrupt, timeout, ignorePrevious, &status);
-  wpi_setHALError(status);
-
-  // Rising edge result is the interrupt bit set in the byte 0xFF
-  // Falling edge result is the interrupt bit set in the byte 0xFF00
-  // Set any bit set to be true for that edge, and AND the 2 results
-  // together to match the existing enum for all interrupts
-  int32_t rising = (result & 0xFF) ? 0x1 : 0x0;
-  int32_t falling = ((result & 0xFF00) ? 0x0100 : 0x0);
-  return static_cast<WaitResult>(falling | rising);
-}
-
-void InterruptableSensorBase::EnableInterrupts() {
-  if (StatusIsFatal()) return;
-  wpi_assert(m_interrupt != HAL_kInvalidHandle);
-  int32_t status = 0;
-  HAL_EnableInterrupts(m_interrupt, &status);
-  wpi_setHALError(status);
-}
-
-void InterruptableSensorBase::DisableInterrupts() {
-  if (StatusIsFatal()) return;
-  wpi_assert(m_interrupt != HAL_kInvalidHandle);
-  int32_t status = 0;
-  HAL_DisableInterrupts(m_interrupt, &status);
-  wpi_setHALError(status);
-}
-
-double InterruptableSensorBase::ReadRisingTimestamp() {
-  if (StatusIsFatal()) return 0.0;
-  wpi_assert(m_interrupt != HAL_kInvalidHandle);
-  int32_t status = 0;
-  int64_t timestamp = HAL_ReadInterruptRisingTimestamp(m_interrupt, &status);
-  wpi_setHALError(status);
-  return timestamp * 1e-6;
-}
-
-double InterruptableSensorBase::ReadFallingTimestamp() {
-  if (StatusIsFatal()) return 0.0;
-  wpi_assert(m_interrupt != HAL_kInvalidHandle);
-  int32_t status = 0;
-  int64_t timestamp = HAL_ReadInterruptFallingTimestamp(m_interrupt, &status);
-  wpi_setHALError(status);
-  return timestamp * 1e-6;
-}
-
-void InterruptableSensorBase::SetUpSourceEdge(bool risingEdge,
-                                              bool fallingEdge) {
-  if (StatusIsFatal()) return;
-  if (m_interrupt == HAL_kInvalidHandle) {
-    wpi_setWPIErrorWithContext(
-        NullParameter,
-        "You must call RequestInterrupts before SetUpSourceEdge");
-    return;
-  }
-  if (m_interrupt != HAL_kInvalidHandle) {
-    int32_t status = 0;
-    HAL_SetInterruptUpSourceEdge(m_interrupt, risingEdge, fallingEdge, &status);
-    wpi_setHALError(status);
-  }
-}
-
-void InterruptableSensorBase::AllocateInterrupts(bool watcher) {
-  wpi_assert(m_interrupt == HAL_kInvalidHandle);
-  // Expects the calling leaf class to allocate an interrupt index.
-  int32_t status = 0;
-  m_interrupt = HAL_InitializeInterrupts(watcher, &status);
-  wpi_setHALError(status);
-}
diff --git a/wpilibc/src/main/native/cpp/IterativeRobot.cpp b/wpilibc/src/main/native/cpp/IterativeRobot.cpp
deleted file mode 100644
index 72fb79b..0000000
--- a/wpilibc/src/main/native/cpp/IterativeRobot.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/IterativeRobot.h"
-
-#include <hal/DriverStation.h>
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/DriverStation.h"
-
-using namespace frc;
-
-static constexpr auto kPacketPeriod = 0.02_s;
-
-IterativeRobot::IterativeRobot() : IterativeRobotBase(kPacketPeriod) {
-  HAL_Report(HALUsageReporting::kResourceType_Framework,
-             HALUsageReporting::kFramework_Iterative);
-}
-
-void IterativeRobot::StartCompetition() {
-  RobotInit();
-
-  if constexpr (IsSimulation()) {
-    SimulationInit();
-  }
-
-  // Tell the DS that the robot is ready to be enabled
-  HAL_ObserveUserProgramStarting();
-
-  // Loop forever, calling the appropriate mode-dependent function
-  while (true) {
-    // Wait for driver station data so the loop doesn't hog the CPU
-    DriverStation::GetInstance().WaitForData();
-    if (m_exit) break;
-
-    LoopFunc();
-  }
-}
-
-void IterativeRobot::EndCompetition() {
-  m_exit = true;
-  DriverStation::GetInstance().WakeupWaitForData();
-}
diff --git a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
index 7b29130..47eb299 100644
--- a/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
+++ b/wpilibc/src/main/native/cpp/IterativeRobotBase.cpp
@@ -1,18 +1,15 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/IterativeRobotBase.h"
 
+#include <fmt/format.h>
 #include <hal/DriverStation.h>
-#include <wpi/Format.h>
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
+#include <networktables/NetworkTableInstance.h>
 
-#include "frc/DriverStation.h"
+#include "frc/DSControlWord.h"
+#include "frc/Errors.h"
 #include "frc/livewindow/LiveWindow.h"
 #include "frc/shuffleboard/Shuffleboard.h"
 #include "frc/smartdashboard/SmartDashboard.h"
@@ -26,34 +23,22 @@
     : m_period(period),
       m_watchdog(period, [this] { PrintLoopOverrunMessage(); }) {}
 
-void IterativeRobotBase::RobotInit() {
-  wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
-}
+void IterativeRobotBase::RobotInit() {}
 
-void IterativeRobotBase::SimulationInit() {
-  wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
-}
+void IterativeRobotBase::SimulationInit() {}
 
-void IterativeRobotBase::DisabledInit() {
-  wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
-}
+void IterativeRobotBase::DisabledInit() {}
 
-void IterativeRobotBase::AutonomousInit() {
-  wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
-}
+void IterativeRobotBase::AutonomousInit() {}
 
-void IterativeRobotBase::TeleopInit() {
-  wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
-}
+void IterativeRobotBase::TeleopInit() {}
 
-void IterativeRobotBase::TestInit() {
-  wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
-}
+void IterativeRobotBase::TestInit() {}
 
 void IterativeRobotBase::RobotPeriodic() {
   static bool firstRun = true;
   if (firstRun) {
-    wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
+    fmt::print("Default {}() method... Override me!\n", __FUNCTION__);
     firstRun = false;
   }
 }
@@ -61,7 +46,7 @@
 void IterativeRobotBase::SimulationPeriodic() {
   static bool firstRun = true;
   if (firstRun) {
-    wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
+    fmt::print("Default {}() method... Override me!\n", __FUNCTION__);
     firstRun = false;
   }
 }
@@ -69,7 +54,7 @@
 void IterativeRobotBase::DisabledPeriodic() {
   static bool firstRun = true;
   if (firstRun) {
-    wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
+    fmt::print("Default {}() method... Override me!\n", __FUNCTION__);
     firstRun = false;
   }
 }
@@ -77,7 +62,7 @@
 void IterativeRobotBase::AutonomousPeriodic() {
   static bool firstRun = true;
   if (firstRun) {
-    wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
+    fmt::print("Default {}() method... Override me!\n", __FUNCTION__);
     firstRun = false;
   }
 }
@@ -85,7 +70,7 @@
 void IterativeRobotBase::TeleopPeriodic() {
   static bool firstRun = true;
   if (firstRun) {
-    wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
+    fmt::print("Default {}() method... Override me!\n", __FUNCTION__);
     firstRun = false;
   }
 }
@@ -93,68 +78,92 @@
 void IterativeRobotBase::TestPeriodic() {
   static bool firstRun = true;
   if (firstRun) {
-    wpi::outs() << "Default " << __FUNCTION__ << "() method... Override me!\n";
+    fmt::print("Default {}() method... Override me!\n", __FUNCTION__);
     firstRun = false;
   }
 }
 
+void IterativeRobotBase::DisabledExit() {}
+
+void IterativeRobotBase::AutonomousExit() {}
+
+void IterativeRobotBase::TeleopExit() {}
+
+void IterativeRobotBase::TestExit() {}
+
+void IterativeRobotBase::SetNetworkTablesFlushEnabled(bool enabled) {
+  m_ntFlushEnabled = enabled;
+}
+
+units::second_t IterativeRobotBase::GetPeriod() const {
+  return m_period;
+}
+
 void IterativeRobotBase::LoopFunc() {
   m_watchdog.Reset();
 
-  // Call the appropriate function depending upon the current robot mode
-  if (IsDisabled()) {
-    // Call DisabledInit() if we are now just entering disabled mode from
-    // either a different mode or from power-on.
-    if (m_lastMode != Mode::kDisabled) {
-      LiveWindow::GetInstance()->SetEnabled(false);
+  // Get current mode
+  DSControlWord word;
+  Mode mode = Mode::kNone;
+  if (word.IsDisabled()) {
+    mode = Mode::kDisabled;
+  } else if (word.IsAutonomous()) {
+    mode = Mode::kAutonomous;
+  } else if (word.IsTeleop()) {
+    mode = Mode::kTeleop;
+  } else if (word.IsTest()) {
+    mode = Mode::kTest;
+  }
+
+  // If mode changed, call mode exit and entry functions
+  if (m_lastMode != mode) {
+    // Call last mode's exit function
+    if (m_lastMode == Mode::kDisabled) {
+      DisabledExit();
+    } else if (m_lastMode == Mode::kAutonomous) {
+      AutonomousExit();
+    } else if (m_lastMode == Mode::kTeleop) {
+      TeleopExit();
+    } else if (m_lastMode == Mode::kTest) {
+      LiveWindow::SetEnabled(false);
       Shuffleboard::DisableActuatorWidgets();
+      TestExit();
+    }
+
+    // Call current mode's entry function
+    if (mode == Mode::kDisabled) {
       DisabledInit();
       m_watchdog.AddEpoch("DisabledInit()");
-      m_lastMode = Mode::kDisabled;
-    }
-
-    HAL_ObserveUserProgramDisabled();
-    DisabledPeriodic();
-    m_watchdog.AddEpoch("DisabledPeriodic()");
-  } else if (IsAutonomous()) {
-    // Call AutonomousInit() if we are now just entering autonomous mode from
-    // either a different mode or from power-on.
-    if (m_lastMode != Mode::kAutonomous) {
-      LiveWindow::GetInstance()->SetEnabled(false);
-      Shuffleboard::DisableActuatorWidgets();
+    } else if (mode == Mode::kAutonomous) {
       AutonomousInit();
       m_watchdog.AddEpoch("AutonomousInit()");
-      m_lastMode = Mode::kAutonomous;
-    }
-
-    HAL_ObserveUserProgramAutonomous();
-    AutonomousPeriodic();
-    m_watchdog.AddEpoch("AutonomousPeriodic()");
-  } else if (IsOperatorControl()) {
-    // Call TeleopInit() if we are now just entering teleop mode from
-    // either a different mode or from power-on.
-    if (m_lastMode != Mode::kTeleop) {
-      LiveWindow::GetInstance()->SetEnabled(false);
-      Shuffleboard::DisableActuatorWidgets();
+    } else if (mode == Mode::kTeleop) {
       TeleopInit();
       m_watchdog.AddEpoch("TeleopInit()");
-      m_lastMode = Mode::kTeleop;
-    }
-
-    HAL_ObserveUserProgramTeleop();
-    TeleopPeriodic();
-    m_watchdog.AddEpoch("TeleopPeriodic()");
-  } else {
-    // Call TestInit() if we are now just entering test mode from
-    // either a different mode or from power-on.
-    if (m_lastMode != Mode::kTest) {
-      LiveWindow::GetInstance()->SetEnabled(true);
+    } else if (mode == Mode::kTest) {
+      LiveWindow::SetEnabled(true);
       Shuffleboard::EnableActuatorWidgets();
       TestInit();
       m_watchdog.AddEpoch("TestInit()");
-      m_lastMode = Mode::kTest;
     }
 
+    m_lastMode = mode;
+  }
+
+  // Call the appropriate function depending upon the current robot mode
+  if (mode == Mode::kDisabled) {
+    HAL_ObserveUserProgramDisabled();
+    DisabledPeriodic();
+    m_watchdog.AddEpoch("DisabledPeriodic()");
+  } else if (mode == Mode::kAutonomous) {
+    HAL_ObserveUserProgramAutonomous();
+    AutonomousPeriodic();
+    m_watchdog.AddEpoch("AutonomousPeriodic()");
+  } else if (mode == Mode::kTeleop) {
+    HAL_ObserveUserProgramTeleop();
+    TeleopPeriodic();
+    m_watchdog.AddEpoch("TeleopPeriodic()");
+  } else if (mode == Mode::kTest) {
     HAL_ObserveUserProgramTest();
     TestPeriodic();
     m_watchdog.AddEpoch("TestPeriodic()");
@@ -165,18 +174,25 @@
 
   SmartDashboard::UpdateValues();
   m_watchdog.AddEpoch("SmartDashboard::UpdateValues()");
-  LiveWindow::GetInstance()->UpdateValues();
+  LiveWindow::UpdateValues();
   m_watchdog.AddEpoch("LiveWindow::UpdateValues()");
   Shuffleboard::Update();
   m_watchdog.AddEpoch("Shuffleboard::Update()");
 
   if constexpr (IsSimulation()) {
+    HAL_SimPeriodicBefore();
     SimulationPeriodic();
+    HAL_SimPeriodicAfter();
     m_watchdog.AddEpoch("SimulationPeriodic()");
   }
 
   m_watchdog.Disable();
 
+  // Flush NetworkTables
+  if (m_ntFlushEnabled) {
+    nt::NetworkTableInstance::GetDefault().Flush();
+  }
+
   // Warn on loop time overruns
   if (m_watchdog.IsExpired()) {
     m_watchdog.PrintEpochs();
@@ -184,11 +200,5 @@
 }
 
 void IterativeRobotBase::PrintLoopOverrunMessage() {
-  wpi::SmallString<128> str;
-  wpi::raw_svector_ostream buf(str);
-
-  buf << "Loop time of " << wpi::format("%.6f", m_period.to<double>())
-      << "s overrun\n";
-
-  DriverStation::ReportWarning(str);
+  FRC_ReportError(err::Error, "Loop time of {:.6f}s overrun", m_period.value());
 }
diff --git a/wpilibc/src/main/native/cpp/Jaguar.cpp b/wpilibc/src/main/native/cpp/Jaguar.cpp
deleted file mode 100644
index c87e4d1..0000000
--- a/wpilibc/src/main/native/cpp/Jaguar.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/Jaguar.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-Jaguar::Jaguar(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.31, 1.55, 1.507, 1.454, 0.697);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_Jaguar, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "Jaguar", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/Joystick.cpp b/wpilibc/src/main/native/cpp/Joystick.cpp
index c2cdaea..48f0c77 100644
--- a/wpilibc/src/main/native/cpp/Joystick.cpp
+++ b/wpilibc/src/main/native/cpp/Joystick.cpp
@@ -1,16 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Joystick.h"
 
 #include <cmath>
 
 #include <hal/FRCUsageReporting.h>
-#include <wpi/math>
+#include <wpi/numbers>
 
 using namespace frc;
 
@@ -24,45 +21,69 @@
   HAL_Report(HALUsageReporting::kResourceType_Joystick, port + 1);
 }
 
-void Joystick::SetXChannel(int channel) { m_axes[Axis::kX] = channel; }
+void Joystick::SetXChannel(int channel) {
+  m_axes[Axis::kX] = channel;
+}
 
-void Joystick::SetYChannel(int channel) { m_axes[Axis::kY] = channel; }
+void Joystick::SetYChannel(int channel) {
+  m_axes[Axis::kY] = channel;
+}
 
-void Joystick::SetZChannel(int channel) { m_axes[Axis::kZ] = channel; }
+void Joystick::SetZChannel(int channel) {
+  m_axes[Axis::kZ] = channel;
+}
 
-void Joystick::SetTwistChannel(int channel) { m_axes[Axis::kTwist] = channel; }
+void Joystick::SetTwistChannel(int channel) {
+  m_axes[Axis::kTwist] = channel;
+}
 
 void Joystick::SetThrottleChannel(int channel) {
   m_axes[Axis::kThrottle] = channel;
 }
 
-int Joystick::GetXChannel() const { return m_axes[Axis::kX]; }
+int Joystick::GetXChannel() const {
+  return m_axes[Axis::kX];
+}
 
-int Joystick::GetYChannel() const { return m_axes[Axis::kY]; }
+int Joystick::GetYChannel() const {
+  return m_axes[Axis::kY];
+}
 
-int Joystick::GetZChannel() const { return m_axes[Axis::kZ]; }
+int Joystick::GetZChannel() const {
+  return m_axes[Axis::kZ];
+}
 
-int Joystick::GetTwistChannel() const { return m_axes[Axis::kTwist]; }
+int Joystick::GetTwistChannel() const {
+  return m_axes[Axis::kTwist];
+}
 
-int Joystick::GetThrottleChannel() const { return m_axes[Axis::kThrottle]; }
+int Joystick::GetThrottleChannel() const {
+  return m_axes[Axis::kThrottle];
+}
 
-double Joystick::GetX(JoystickHand hand) const {
+double Joystick::GetX() const {
   return GetRawAxis(m_axes[Axis::kX]);
 }
 
-double Joystick::GetY(JoystickHand hand) const {
+double Joystick::GetY() const {
   return GetRawAxis(m_axes[Axis::kY]);
 }
 
-double Joystick::GetZ() const { return GetRawAxis(m_axes[Axis::kZ]); }
+double Joystick::GetZ() const {
+  return GetRawAxis(m_axes[Axis::kZ]);
+}
 
-double Joystick::GetTwist() const { return GetRawAxis(m_axes[Axis::kTwist]); }
+double Joystick::GetTwist() const {
+  return GetRawAxis(m_axes[Axis::kTwist]);
+}
 
 double Joystick::GetThrottle() const {
   return GetRawAxis(m_axes[Axis::kThrottle]);
 }
 
-bool Joystick::GetTrigger() const { return GetRawButton(Button::kTrigger); }
+bool Joystick::GetTrigger() const {
+  return GetRawButton(Button::kTrigger);
+}
 
 bool Joystick::GetTriggerPressed() {
   return GetRawButtonPressed(Button::kTrigger);
@@ -72,11 +93,17 @@
   return GetRawButtonReleased(Button::kTrigger);
 }
 
-bool Joystick::GetTop() const { return GetRawButton(Button::kTop); }
+bool Joystick::GetTop() const {
+  return GetRawButton(Button::kTop);
+}
 
-bool Joystick::GetTopPressed() { return GetRawButtonPressed(Button::kTop); }
+bool Joystick::GetTopPressed() {
+  return GetRawButtonPressed(Button::kTop);
+}
 
-bool Joystick::GetTopReleased() { return GetRawButtonReleased(Button::kTop); }
+bool Joystick::GetTopReleased() {
+  return GetRawButtonReleased(Button::kTop);
+}
 
 double Joystick::GetMagnitude() const {
   return std::sqrt(std::pow(GetX(), 2) + std::pow(GetY(), 2));
@@ -87,5 +114,5 @@
 }
 
 double Joystick::GetDirectionDegrees() const {
-  return (180 / wpi::math::pi) * GetDirectionRadians();
+  return (180 / wpi::numbers::pi) * GetDirectionRadians();
 }
diff --git a/wpilibc/src/main/native/cpp/MotorSafety.cpp b/wpilibc/src/main/native/cpp/MotorSafety.cpp
index 1806e86..9cb28cd 100644
--- a/wpilibc/src/main/native/cpp/MotorSafety.cpp
+++ b/wpilibc/src/main/native/cpp/MotorSafety.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/MotorSafety.h"
 
@@ -11,11 +8,9 @@
 #include <utility>
 
 #include <wpi/SmallPtrSet.h>
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
 
 #include "frc/DriverStation.h"
-#include "frc/WPIErrors.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
@@ -33,16 +28,13 @@
 }
 
 MotorSafety::MotorSafety(MotorSafety&& rhs)
-    : ErrorBase(std::move(rhs)),
-      m_expiration(std::move(rhs.m_expiration)),
+    : m_expiration(std::move(rhs.m_expiration)),
       m_enabled(std::move(rhs.m_enabled)),
       m_stopTime(std::move(rhs.m_stopTime)) {}
 
 MotorSafety& MotorSafety::operator=(MotorSafety&& rhs) {
   std::scoped_lock lock(m_thisMutex, rhs.m_thisMutex);
 
-  ErrorBase::operator=(std::move(rhs));
-
   m_expiration = std::move(rhs.m_expiration);
   m_enabled = std::move(rhs.m_enabled);
   m_stopTime = std::move(rhs.m_stopTime);
@@ -55,12 +47,12 @@
   m_stopTime = Timer::GetFPGATimestamp() + m_expiration;
 }
 
-void MotorSafety::SetExpiration(double expirationTime) {
+void MotorSafety::SetExpiration(units::second_t expirationTime) {
   std::scoped_lock lock(m_thisMutex);
   m_expiration = expirationTime;
 }
 
-double MotorSafety::GetExpiration() const {
+units::second_t MotorSafety::GetExpiration() const {
   std::scoped_lock lock(m_thisMutex);
   return m_expiration;
 }
@@ -82,7 +74,7 @@
 
 void MotorSafety::Check() {
   bool enabled;
-  double stopTime;
+  units::second_t stopTime;
 
   {
     std::scoped_lock lock(m_thisMutex);
@@ -90,17 +82,13 @@
     stopTime = m_stopTime;
   }
 
-  DriverStation& ds = DriverStation::GetInstance();
-  if (!enabled || ds.IsDisabled() || ds.IsTest()) {
+  if (!enabled || DriverStation::IsDisabled() || DriverStation::IsTest()) {
     return;
   }
 
   if (stopTime < Timer::GetFPGATimestamp()) {
-    wpi::SmallString<128> buf;
-    wpi::raw_svector_ostream desc(buf);
-    GetDescription(desc);
-    desc << "... Output not updated often enough.";
-    wpi_setWPIErrorWithContext(Timeout, desc.str());
+    FRC_ReportError(err::Timeout, "{}... Output not updated often enough",
+                    GetDescription());
     StopMotor();
   }
 }
diff --git a/wpilibc/src/main/native/cpp/NidecBrushless.cpp b/wpilibc/src/main/native/cpp/NidecBrushless.cpp
deleted file mode 100644
index 82a278e..0000000
--- a/wpilibc/src/main/native/cpp/NidecBrushless.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/NidecBrushless.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-NidecBrushless::NidecBrushless(int pwmChannel, int dioChannel)
-    : m_dio(dioChannel), m_pwm(pwmChannel) {
-  auto& registry = SendableRegistry::GetInstance();
-  registry.AddChild(this, &m_dio);
-  registry.AddChild(this, &m_pwm);
-  SetExpiration(0.0);
-  SetSafetyEnabled(false);
-
-  // the dio controls the output (in PWM mode)
-  m_dio.SetPWMRate(15625);
-  m_dio.EnablePWM(0.5);
-
-  HAL_Report(HALUsageReporting::kResourceType_NidecBrushless, pwmChannel + 1);
-  registry.AddLW(this, "Nidec Brushless", pwmChannel);
-}
-
-void NidecBrushless::Set(double speed) {
-  if (!m_disabled) {
-    m_speed = speed;
-    m_dio.UpdateDutyCycle(0.5 + 0.5 * (m_isInverted ? -speed : speed));
-    m_pwm.SetRaw(0xffff);
-  }
-  Feed();
-}
-
-double NidecBrushless::Get() const { return m_speed; }
-
-void NidecBrushless::SetInverted(bool isInverted) { m_isInverted = isInverted; }
-
-bool NidecBrushless::GetInverted() const { return m_isInverted; }
-
-void NidecBrushless::Disable() {
-  m_disabled = true;
-  m_dio.UpdateDutyCycle(0.5);
-  m_pwm.SetDisabled();
-}
-
-void NidecBrushless::Enable() { m_disabled = false; }
-
-void NidecBrushless::PIDWrite(double output) { Set(output); }
-
-void NidecBrushless::StopMotor() {
-  m_dio.UpdateDutyCycle(0.5);
-  m_pwm.SetDisabled();
-}
-
-void NidecBrushless::GetDescription(wpi::raw_ostream& desc) const {
-  desc << "Nidec " << GetChannel();
-}
-
-int NidecBrushless::GetChannel() const { return m_pwm.GetChannel(); }
-
-void NidecBrushless::InitSendable(SendableBuilder& builder) {
-  builder.SetSmartDashboardType("Nidec Brushless");
-  builder.SetActuator(true);
-  builder.SetSafeState([=]() { StopMotor(); });
-  builder.AddDoubleProperty(
-      "Value", [=]() { return Get(); }, [=](double value) { Set(value); });
-}
diff --git a/wpilibc/src/main/native/cpp/Notifier.cpp b/wpilibc/src/main/native/cpp/Notifier.cpp
index 4280ce4..441f750 100644
--- a/wpilibc/src/main/native/cpp/Notifier.cpp
+++ b/wpilibc/src/main/native/cpp/Notifier.cpp
@@ -1,40 +1,41 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Notifier.h"
 
 #include <utility>
 
+#include <fmt/format.h>
 #include <hal/FRCUsageReporting.h>
 #include <hal/Notifier.h>
 #include <hal/Threads.h>
-#include <wpi/SmallString.h>
 
+#include "frc/Errors.h"
 #include "frc/Timer.h"
-#include "frc/Utility.h"
-#include "frc/WPIErrors.h"
 
 using namespace frc;
 
 Notifier::Notifier(std::function<void()> handler) {
-  if (handler == nullptr)
-    wpi_setWPIErrorWithContext(NullParameter, "handler must not be nullptr");
+  if (!handler) {
+    throw FRC_MakeError(err::NullParameter, "{}", "handler");
+  }
   m_handler = handler;
   int32_t status = 0;
   m_notifier = HAL_InitializeNotifier(&status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "InitializeNotifier");
 
   m_thread = std::thread([=] {
     for (;;) {
       int32_t status = 0;
       HAL_NotifierHandle notifier = m_notifier.load();
-      if (notifier == 0) break;
+      if (notifier == 0) {
+        break;
+      }
       uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
-      if (curTime == 0 || status != 0) break;
+      if (curTime == 0 || status != 0) {
+        break;
+      }
 
       std::function<void()> handler;
       {
@@ -50,27 +51,34 @@
       }
 
       // call callback
-      if (handler) handler();
+      if (handler) {
+        handler();
+      }
     }
   });
 }
 
 Notifier::Notifier(int priority, std::function<void()> handler) {
-  if (handler == nullptr)
-    wpi_setWPIErrorWithContext(NullParameter, "handler must not be nullptr");
+  if (!handler) {
+    throw FRC_MakeError(err::NullParameter, "{}", "handler");
+  }
   m_handler = handler;
   int32_t status = 0;
   m_notifier = HAL_InitializeNotifier(&status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "InitializeNotifier");
 
   m_thread = std::thread([=] {
     int32_t status = 0;
     HAL_SetCurrentThreadPriority(true, priority, &status);
     for (;;) {
       HAL_NotifierHandle notifier = m_notifier.load();
-      if (notifier == 0) break;
+      if (notifier == 0) {
+        break;
+      }
       uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
-      if (curTime == 0 || status != 0) break;
+      if (curTime == 0 || status != 0) {
+        break;
+      }
 
       std::function<void()> handler;
       {
@@ -86,7 +94,9 @@
       }
 
       // call callback
-      if (handler) handler();
+      if (handler) {
+        handler();
+      }
     }
   });
 }
@@ -96,17 +106,18 @@
   // atomically set handle to 0, then clean
   HAL_NotifierHandle handle = m_notifier.exchange(0);
   HAL_StopNotifier(handle, &status);
-  wpi_setHALError(status);
+  FRC_ReportError(status, "{}", "StopNotifier");
 
   // Join the thread to ensure the handler has exited.
-  if (m_thread.joinable()) m_thread.join();
+  if (m_thread.joinable()) {
+    m_thread.join();
+  }
 
   HAL_CleanNotifier(handle, &status);
 }
 
 Notifier::Notifier(Notifier&& rhs)
-    : ErrorBase(std::move(rhs)),
-      m_thread(std::move(rhs.m_thread)),
+    : m_thread(std::move(rhs.m_thread)),
       m_notifier(rhs.m_notifier.load()),
       m_handler(std::move(rhs.m_handler)),
       m_expirationTime(std::move(rhs.m_expirationTime)),
@@ -116,8 +127,6 @@
 }
 
 Notifier& Notifier::operator=(Notifier&& rhs) {
-  ErrorBase::operator=(std::move(rhs));
-
   m_thread = std::move(rhs.m_thread);
   m_notifier = rhs.m_notifier.load();
   rhs.m_notifier = HAL_kInvalidHandle;
@@ -129,11 +138,12 @@
   return *this;
 }
 
-void Notifier::SetName(const wpi::Twine& name) {
-  wpi::SmallString<64> nameBuf;
+void Notifier::SetName(std::string_view name) {
+  fmt::memory_buffer buf;
+  fmt::format_to(fmt::appender{buf}, "{}", name);
+  buf.push_back('\0');  // null terminate
   int32_t status = 0;
-  HAL_SetNotifierName(m_notifier,
-                      name.toNullTerminatedStringRef(nameBuf).data(), &status);
+  HAL_SetNotifierName(m_notifier, buf.data(), &status);
 }
 
 void Notifier::SetHandler(std::function<void()> handler) {
@@ -141,26 +151,18 @@
   m_handler = handler;
 }
 
-void Notifier::StartSingle(double delay) {
-  StartSingle(units::second_t(delay));
-}
-
 void Notifier::StartSingle(units::second_t delay) {
   std::scoped_lock lock(m_processMutex);
   m_periodic = false;
-  m_period = delay.to<double>();
+  m_period = delay;
   m_expirationTime = Timer::GetFPGATimestamp() + m_period;
   UpdateAlarm();
 }
 
-void Notifier::StartPeriodic(double period) {
-  StartPeriodic(units::second_t(period));
-}
-
 void Notifier::StartPeriodic(units::second_t period) {
   std::scoped_lock lock(m_processMutex);
   m_periodic = true;
-  m_period = period.to<double>();
+  m_period = period;
   m_expirationTime = Timer::GetFPGATimestamp() + m_period;
   UpdateAlarm();
 }
@@ -170,18 +172,25 @@
   m_periodic = false;
   int32_t status = 0;
   HAL_CancelNotifierAlarm(m_notifier, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "CancelNotifierAlarm");
 }
 
 void Notifier::UpdateAlarm(uint64_t triggerTime) {
   int32_t status = 0;
   // Return if we are being destructed, or were not created successfully
   auto notifier = m_notifier.load();
-  if (notifier == 0) return;
+  if (notifier == 0) {
+    return;
+  }
   HAL_UpdateNotifierAlarm(notifier, triggerTime, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "UpdateNotifierAlarm");
 }
 
 void Notifier::UpdateAlarm() {
   UpdateAlarm(static_cast<uint64_t>(m_expirationTime * 1e6));
 }
+
+bool Notifier::SetHALThreadPriority(bool realTime, int32_t priority) {
+  int32_t status = 0;
+  return HAL_SetNotifierThreadPriority(realTime, priority, &status);
+}
diff --git a/wpilibc/src/main/native/cpp/PIDBase.cpp b/wpilibc/src/main/native/cpp/PIDBase.cpp
deleted file mode 100644
index 5802d98..0000000
--- a/wpilibc/src/main/native/cpp/PIDBase.cpp
+++ /dev/null
@@ -1,354 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PIDBase.h"
-
-#include <algorithm>
-#include <cmath>
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/PIDOutput.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-template <class T>
-constexpr const T& clamp(const T& value, const T& low, const T& high) {
-  return std::max(low, std::min(value, high));
-}
-
-PIDBase::PIDBase(double Kp, double Ki, double Kd, PIDSource& source,
-                 PIDOutput& output)
-    : PIDBase(Kp, Ki, Kd, 0.0, source, output) {}
-
-PIDBase::PIDBase(double Kp, double Ki, double Kd, double Kf, PIDSource& source,
-                 PIDOutput& output) {
-  m_P = Kp;
-  m_I = Ki;
-  m_D = Kd;
-  m_F = Kf;
-
-  m_pidInput = &source;
-  m_filter = LinearFilter<double>::MovingAverage(1);
-
-  m_pidOutput = &output;
-
-  m_setpointTimer.Start();
-
-  static int instances = 0;
-  instances++;
-  HAL_Report(HALUsageReporting::kResourceType_PIDController, instances);
-  SendableRegistry::GetInstance().Add(this, "PIDController", instances);
-}
-
-double PIDBase::Get() const {
-  std::scoped_lock lock(m_thisMutex);
-  return m_result;
-}
-
-void PIDBase::SetContinuous(bool continuous) {
-  std::scoped_lock lock(m_thisMutex);
-  m_continuous = continuous;
-}
-
-void PIDBase::SetInputRange(double minimumInput, double maximumInput) {
-  {
-    std::scoped_lock lock(m_thisMutex);
-    m_minimumInput = minimumInput;
-    m_maximumInput = maximumInput;
-    m_inputRange = maximumInput - minimumInput;
-  }
-
-  SetSetpoint(m_setpoint);
-}
-
-void PIDBase::SetOutputRange(double minimumOutput, double maximumOutput) {
-  std::scoped_lock lock(m_thisMutex);
-  m_minimumOutput = minimumOutput;
-  m_maximumOutput = maximumOutput;
-}
-
-void PIDBase::SetPID(double p, double i, double d) {
-  {
-    std::scoped_lock lock(m_thisMutex);
-    m_P = p;
-    m_I = i;
-    m_D = d;
-  }
-}
-
-void PIDBase::SetPID(double p, double i, double d, double f) {
-  std::scoped_lock lock(m_thisMutex);
-  m_P = p;
-  m_I = i;
-  m_D = d;
-  m_F = f;
-}
-
-void PIDBase::SetP(double p) {
-  std::scoped_lock lock(m_thisMutex);
-  m_P = p;
-}
-
-void PIDBase::SetI(double i) {
-  std::scoped_lock lock(m_thisMutex);
-  m_I = i;
-}
-
-void PIDBase::SetD(double d) {
-  std::scoped_lock lock(m_thisMutex);
-  m_D = d;
-}
-
-void PIDBase::SetF(double f) {
-  std::scoped_lock lock(m_thisMutex);
-  m_F = f;
-}
-
-double PIDBase::GetP() const {
-  std::scoped_lock lock(m_thisMutex);
-  return m_P;
-}
-
-double PIDBase::GetI() const {
-  std::scoped_lock lock(m_thisMutex);
-  return m_I;
-}
-
-double PIDBase::GetD() const {
-  std::scoped_lock lock(m_thisMutex);
-  return m_D;
-}
-
-double PIDBase::GetF() const {
-  std::scoped_lock lock(m_thisMutex);
-  return m_F;
-}
-
-void PIDBase::SetSetpoint(double setpoint) {
-  {
-    std::scoped_lock lock(m_thisMutex);
-
-    if (m_maximumInput > m_minimumInput) {
-      if (setpoint > m_maximumInput)
-        m_setpoint = m_maximumInput;
-      else if (setpoint < m_minimumInput)
-        m_setpoint = m_minimumInput;
-      else
-        m_setpoint = setpoint;
-    } else {
-      m_setpoint = setpoint;
-    }
-  }
-}
-
-double PIDBase::GetSetpoint() const {
-  std::scoped_lock lock(m_thisMutex);
-  return m_setpoint;
-}
-
-double PIDBase::GetDeltaSetpoint() const {
-  std::scoped_lock lock(m_thisMutex);
-  return (m_setpoint - m_prevSetpoint) / m_setpointTimer.Get();
-}
-
-double PIDBase::GetError() const {
-  double setpoint = GetSetpoint();
-  {
-    std::scoped_lock lock(m_thisMutex);
-    return GetContinuousError(setpoint - m_pidInput->PIDGet());
-  }
-}
-
-double PIDBase::GetAvgError() const { return GetError(); }
-
-void PIDBase::SetPIDSourceType(PIDSourceType pidSource) {
-  m_pidInput->SetPIDSourceType(pidSource);
-}
-
-PIDSourceType PIDBase::GetPIDSourceType() const {
-  return m_pidInput->GetPIDSourceType();
-}
-
-void PIDBase::SetTolerance(double percent) {
-  std::scoped_lock lock(m_thisMutex);
-  m_toleranceType = kPercentTolerance;
-  m_tolerance = percent;
-}
-
-void PIDBase::SetAbsoluteTolerance(double absTolerance) {
-  std::scoped_lock lock(m_thisMutex);
-  m_toleranceType = kAbsoluteTolerance;
-  m_tolerance = absTolerance;
-}
-
-void PIDBase::SetPercentTolerance(double percent) {
-  std::scoped_lock lock(m_thisMutex);
-  m_toleranceType = kPercentTolerance;
-  m_tolerance = percent;
-}
-
-void PIDBase::SetToleranceBuffer(int bufLength) {
-  std::scoped_lock lock(m_thisMutex);
-  m_filter = LinearFilter<double>::MovingAverage(bufLength);
-}
-
-bool PIDBase::OnTarget() const {
-  double error = GetError();
-
-  std::scoped_lock lock(m_thisMutex);
-  switch (m_toleranceType) {
-    case kPercentTolerance:
-      return std::fabs(error) < m_tolerance / 100 * m_inputRange;
-      break;
-    case kAbsoluteTolerance:
-      return std::fabs(error) < m_tolerance;
-      break;
-    case kNoTolerance:
-      // TODO: this case needs an error
-      return false;
-  }
-  return false;
-}
-
-void PIDBase::Reset() {
-  std::scoped_lock lock(m_thisMutex);
-  m_prevError = 0;
-  m_totalError = 0;
-  m_result = 0;
-}
-
-void PIDBase::PIDWrite(double output) { SetSetpoint(output); }
-
-void PIDBase::InitSendable(SendableBuilder& builder) {
-  builder.SetSmartDashboardType("PIDController");
-  builder.SetSafeState([=]() { Reset(); });
-  builder.AddDoubleProperty(
-      "p", [=]() { return GetP(); }, [=](double value) { SetP(value); });
-  builder.AddDoubleProperty(
-      "i", [=]() { return GetI(); }, [=](double value) { SetI(value); });
-  builder.AddDoubleProperty(
-      "d", [=]() { return GetD(); }, [=](double value) { SetD(value); });
-  builder.AddDoubleProperty(
-      "f", [=]() { return GetF(); }, [=](double value) { SetF(value); });
-  builder.AddDoubleProperty(
-      "setpoint", [=]() { return GetSetpoint(); },
-      [=](double value) { SetSetpoint(value); });
-}
-
-void PIDBase::Calculate() {
-  if (m_pidInput == nullptr || m_pidOutput == nullptr) return;
-
-  bool enabled;
-  {
-    std::scoped_lock lock(m_thisMutex);
-    enabled = m_enabled;
-  }
-
-  if (enabled) {
-    double input;
-
-    // Storage for function inputs
-    PIDSourceType pidSourceType;
-    double P;
-    double I;
-    double D;
-    double feedForward = CalculateFeedForward();
-    double minimumOutput;
-    double maximumOutput;
-
-    // Storage for function input-outputs
-    double prevError;
-    double error;
-    double totalError;
-
-    {
-      std::scoped_lock lock(m_thisMutex);
-
-      input = m_filter.Calculate(m_pidInput->PIDGet());
-
-      pidSourceType = m_pidInput->GetPIDSourceType();
-      P = m_P;
-      I = m_I;
-      D = m_D;
-      minimumOutput = m_minimumOutput;
-      maximumOutput = m_maximumOutput;
-
-      prevError = m_prevError;
-      error = GetContinuousError(m_setpoint - input);
-      totalError = m_totalError;
-    }
-
-    // Storage for function outputs
-    double result;
-
-    if (pidSourceType == PIDSourceType::kRate) {
-      if (P != 0) {
-        totalError =
-            clamp(totalError + error, minimumOutput / P, maximumOutput / P);
-      }
-
-      result = D * error + P * totalError + feedForward;
-    } else {
-      if (I != 0) {
-        totalError =
-            clamp(totalError + error, minimumOutput / I, maximumOutput / I);
-      }
-
-      result =
-          P * error + I * totalError + D * (error - prevError) + feedForward;
-    }
-
-    result = clamp(result, minimumOutput, maximumOutput);
-
-    {
-      // Ensures m_enabled check and PIDWrite() call occur atomically
-      std::scoped_lock pidWriteLock(m_pidWriteMutex);
-      std::unique_lock mainLock(m_thisMutex);
-      if (m_enabled) {
-        // Don't block other PIDBase operations on PIDWrite()
-        mainLock.unlock();
-
-        m_pidOutput->PIDWrite(result);
-      }
-    }
-
-    std::scoped_lock lock(m_thisMutex);
-    m_prevError = m_error;
-    m_error = error;
-    m_totalError = totalError;
-    m_result = result;
-  }
-}
-
-double PIDBase::CalculateFeedForward() {
-  if (m_pidInput->GetPIDSourceType() == PIDSourceType::kRate) {
-    return m_F * GetSetpoint();
-  } else {
-    double temp = m_F * GetDeltaSetpoint();
-    m_prevSetpoint = m_setpoint;
-    m_setpointTimer.Reset();
-    return temp;
-  }
-}
-
-double PIDBase::GetContinuousError(double error) const {
-  if (m_continuous && m_inputRange != 0) {
-    error = std::fmod(error, m_inputRange);
-    if (std::fabs(error) > m_inputRange / 2) {
-      if (error > 0) {
-        return error - m_inputRange;
-      } else {
-        return error + m_inputRange;
-      }
-    }
-  }
-
-  return error;
-}
diff --git a/wpilibc/src/main/native/cpp/PIDController.cpp b/wpilibc/src/main/native/cpp/PIDController.cpp
deleted file mode 100644
index a90a645..0000000
--- a/wpilibc/src/main/native/cpp/PIDController.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PIDController.h"
-
-#include "frc/Notifier.h"
-#include "frc/PIDOutput.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-
-using namespace frc;
-
-PIDController::PIDController(double Kp, double Ki, double Kd, PIDSource* source,
-                             PIDOutput* output, double period)
-    : PIDController(Kp, Ki, Kd, 0.0, *source, *output, period) {}
-
-PIDController::PIDController(double Kp, double Ki, double Kd, double Kf,
-                             PIDSource* source, PIDOutput* output,
-                             double period)
-    : PIDController(Kp, Ki, Kd, Kf, *source, *output, period) {}
-
-PIDController::PIDController(double Kp, double Ki, double Kd, PIDSource& source,
-                             PIDOutput& output, double period)
-    : PIDController(Kp, Ki, Kd, 0.0, source, output, period) {}
-
-PIDController::PIDController(double Kp, double Ki, double Kd, double Kf,
-                             PIDSource& source, PIDOutput& output,
-                             double period)
-    : PIDBase(Kp, Ki, Kd, Kf, source, output) {
-  m_controlLoop = std::make_unique<Notifier>(&PIDController::Calculate, this);
-  m_controlLoop->StartPeriodic(units::second_t(period));
-}
-
-PIDController::~PIDController() {
-  // Forcefully stopping the notifier so the callback can successfully run.
-  m_controlLoop->Stop();
-}
-
-void PIDController::Enable() {
-  {
-    std::scoped_lock lock(m_thisMutex);
-    m_enabled = true;
-  }
-}
-
-void PIDController::Disable() {
-  {
-    // Ensures m_enabled modification and PIDWrite() call occur atomically
-    std::scoped_lock pidWriteLock(m_pidWriteMutex);
-    {
-      std::scoped_lock mainLock(m_thisMutex);
-      m_enabled = false;
-    }
-
-    m_pidOutput->PIDWrite(0);
-  }
-}
-
-void PIDController::SetEnabled(bool enable) {
-  if (enable) {
-    Enable();
-  } else {
-    Disable();
-  }
-}
-
-bool PIDController::IsEnabled() const {
-  std::scoped_lock lock(m_thisMutex);
-  return m_enabled;
-}
-
-void PIDController::Reset() {
-  Disable();
-
-  PIDBase::Reset();
-}
-
-void PIDController::InitSendable(SendableBuilder& builder) {
-  PIDBase::InitSendable(builder);
-  builder.AddBooleanProperty(
-      "enabled", [=]() { return IsEnabled(); },
-      [=](bool value) { SetEnabled(value); });
-}
diff --git a/wpilibc/src/main/native/cpp/PIDSource.cpp b/wpilibc/src/main/native/cpp/PIDSource.cpp
deleted file mode 100644
index 28291dd..0000000
--- a/wpilibc/src/main/native/cpp/PIDSource.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PIDSource.h"
-
-using namespace frc;
-
-void PIDSource::SetPIDSourceType(PIDSourceType pidSource) {
-  m_pidSource = pidSource;
-}
-
-PIDSourceType PIDSource::GetPIDSourceType() const { return m_pidSource; }
diff --git a/wpilibc/src/main/native/cpp/PS4Controller.cpp b/wpilibc/src/main/native/cpp/PS4Controller.cpp
new file mode 100644
index 0000000..91fd304
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/PS4Controller.cpp
@@ -0,0 +1,205 @@
+// 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.
+
+#include "frc/PS4Controller.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+PS4Controller::PS4Controller(int port) : GenericHID(port) {
+  HAL_Report(HALUsageReporting::kResourceType_PS4Controller, port + 1);
+}
+
+double PS4Controller::GetLeftX() const {
+  return GetRawAxis(Axis::kLeftX);
+}
+
+double PS4Controller::GetRightX() const {
+  return GetRawAxis(Axis::kRightX);
+}
+
+double PS4Controller::GetLeftY() const {
+  return GetRawAxis(Axis::kLeftY);
+}
+
+double PS4Controller::GetRightY() const {
+  return GetRawAxis(Axis::kRightY);
+}
+
+double PS4Controller::GetL2Axis() const {
+  return GetRawAxis(Axis::kL2);
+}
+
+double PS4Controller::GetR2Axis() const {
+  return GetRawAxis(Axis::kR2);
+}
+
+bool PS4Controller::GetSquareButton() const {
+  return GetRawButton(Button::kSquare);
+}
+
+bool PS4Controller::GetSquareButtonPressed() {
+  return GetRawButtonPressed(Button::kSquare);
+}
+
+bool PS4Controller::GetSquareButtonReleased() {
+  return GetRawButtonReleased(Button::kSquare);
+}
+
+bool PS4Controller::GetCrossButton() const {
+  return GetRawButton(Button::kCross);
+}
+
+bool PS4Controller::GetCrossButtonPressed() {
+  return GetRawButtonPressed(Button::kCross);
+}
+
+bool PS4Controller::GetCrossButtonReleased() {
+  return GetRawButtonReleased(Button::kCross);
+}
+
+bool PS4Controller::GetCircleButton() const {
+  return GetRawButton(Button::kCircle);
+}
+
+bool PS4Controller::GetCircleButtonPressed() {
+  return GetRawButtonPressed(Button::kCircle);
+}
+
+bool PS4Controller::GetCircleButtonReleased() {
+  return GetRawButtonReleased(Button::kCircle);
+}
+
+bool PS4Controller::GetTriangleButton() const {
+  return GetRawButton(Button::kTriangle);
+}
+
+bool PS4Controller::GetTriangleButtonPressed() {
+  return GetRawButtonPressed(Button::kTriangle);
+}
+
+bool PS4Controller::GetTriangleButtonReleased() {
+  return GetRawButtonReleased(Button::kTriangle);
+}
+
+bool PS4Controller::GetL1Button() const {
+  return GetRawButton(Button::kL1);
+}
+
+bool PS4Controller::GetL1ButtonPressed() {
+  return GetRawButtonPressed(Button::kL1);
+}
+
+bool PS4Controller::GetL1ButtonReleased() {
+  return GetRawButtonReleased(Button::kL1);
+}
+
+bool PS4Controller::GetR1Button() const {
+  return GetRawButton(Button::kR1);
+}
+
+bool PS4Controller::GetR1ButtonPressed() {
+  return GetRawButtonPressed(Button::kR1);
+}
+
+bool PS4Controller::GetR1ButtonReleased() {
+  return GetRawButtonReleased(Button::kR1);
+}
+
+bool PS4Controller::GetL2Button() const {
+  return GetRawButton(Button::kL2);
+}
+
+bool PS4Controller::GetL2ButtonPressed() {
+  return GetRawButtonPressed(Button::kL2);
+}
+
+bool PS4Controller::GetL2ButtonReleased() {
+  return GetRawButtonReleased(Button::kL2);
+}
+
+bool PS4Controller::GetR2Button() const {
+  return GetRawButton(Button::kR2);
+}
+
+bool PS4Controller::GetR2ButtonPressed() {
+  return GetRawButtonPressed(Button::kR2);
+}
+
+bool PS4Controller::GetR2ButtonReleased() {
+  return GetRawButtonReleased(Button::kR2);
+}
+
+bool PS4Controller::GetShareButton() const {
+  return GetRawButton(Button::kShare);
+}
+
+bool PS4Controller::GetShareButtonPressed() {
+  return GetRawButtonPressed(Button::kShare);
+}
+
+bool PS4Controller::GetShareButtonReleased() {
+  return GetRawButtonReleased(Button::kShare);
+}
+
+bool PS4Controller::GetOptionsButton() const {
+  return GetRawButton(Button::kOptions);
+}
+
+bool PS4Controller::GetOptionsButtonPressed() {
+  return GetRawButtonPressed(Button::kOptions);
+}
+
+bool PS4Controller::GetOptionsButtonReleased() {
+  return GetRawButtonReleased(Button::kOptions);
+}
+
+bool PS4Controller::GetL3Button() const {
+  return GetRawButton(Button::kL3);
+}
+
+bool PS4Controller::GetL3ButtonPressed() {
+  return GetRawButtonPressed(Button::kL3);
+}
+
+bool PS4Controller::GetL3ButtonReleased() {
+  return GetRawButtonReleased(Button::kL3);
+}
+
+bool PS4Controller::GetR3Button() const {
+  return GetRawButton(Button::kR3);
+}
+
+bool PS4Controller::GetR3ButtonPressed() {
+  return GetRawButtonPressed(Button::kR3);
+}
+
+bool PS4Controller::GetR3ButtonReleased() {
+  return GetRawButtonReleased(Button::kR3);
+}
+
+bool PS4Controller::GetPSButton() const {
+  return GetRawButton(Button::kPS);
+}
+
+bool PS4Controller::GetPSButtonPressed() {
+  return GetRawButtonPressed(Button::kPS);
+}
+
+bool PS4Controller::GetPSButtonReleased() {
+  return GetRawButtonReleased(Button::kPS);
+}
+
+bool PS4Controller::GetTouchpad() const {
+  return GetRawButton(Button::kTouchpad);
+}
+
+bool PS4Controller::GetTouchpadPressed() {
+  return GetRawButtonPressed(Button::kTouchpad);
+}
+
+bool PS4Controller::GetTouchpadReleased() {
+  return GetRawButtonReleased(Button::kTouchpad);
+}
diff --git a/wpilibc/src/main/native/cpp/PWM.cpp b/wpilibc/src/main/native/cpp/PWM.cpp
index c2cd2e2..393accc 100644
--- a/wpilibc/src/main/native/cpp/PWM.cpp
+++ b/wpilibc/src/main/native/cpp/PWM.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/PWM.h"
 
@@ -13,123 +10,97 @@
 #include <hal/HALBase.h>
 #include <hal/PWM.h>
 #include <hal/Ports.h>
+#include <wpi/StackTrace.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
-#include "frc/Utility.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
-PWM::PWM(int channel) {
+PWM::PWM(int channel, bool registerSendable) {
   if (!SensorUtil::CheckPWMChannel(channel)) {
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange,
-                               "PWM Channel " + wpi::Twine(channel));
-    return;
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}", channel);
   }
 
+  auto stack = wpi::GetStackTrace(1);
   int32_t status = 0;
-  m_handle = HAL_InitializePWMPort(HAL_GetPort(channel), &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumPWMChannels(), channel);
-    m_channel = std::numeric_limits<int>::max();
-    m_handle = HAL_kInvalidHandle;
-    return;
-  }
+  m_handle =
+      HAL_InitializePWMPort(HAL_GetPort(channel), stack.c_str(), &status);
+  FRC_CheckErrorStatus(status, "Channel {}", channel);
 
   m_channel = channel;
 
   HAL_SetPWMDisabled(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", channel);
   status = 0;
   HAL_SetPWMEliminateDeadband(m_handle, false, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", channel);
 
   HAL_Report(HALUsageReporting::kResourceType_PWM, channel + 1);
-  SendableRegistry::GetInstance().AddLW(this, "PWM", channel);
-
-  SetSafetyEnabled(false);
+  if (registerSendable) {
+    wpi::SendableRegistry::AddLW(this, "PWM", channel);
+  }
 }
 
 PWM::~PWM() {
   int32_t status = 0;
 
   HAL_SetPWMDisabled(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_ReportError(status, "Channel {}", m_channel);
 
   HAL_FreePWMPort(m_handle, &status);
-  wpi_setHALError(status);
-}
-
-void PWM::StopMotor() { SetDisabled(); }
-
-void PWM::GetDescription(wpi::raw_ostream& desc) const {
-  desc << "PWM " << GetChannel();
+  FRC_ReportError(status, "Channel {}", m_channel);
 }
 
 void PWM::SetRaw(uint16_t value) {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
   HAL_SetPWMRaw(m_handle, value, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 uint16_t PWM::GetRaw() const {
-  if (StatusIsFatal()) return 0;
-
   int32_t status = 0;
   uint16_t value = HAL_GetPWMRaw(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 
   return value;
 }
 
 void PWM::SetPosition(double pos) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetPWMPosition(m_handle, pos, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 double PWM::GetPosition() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double position = HAL_GetPWMPosition(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return position;
 }
 
 void PWM::SetSpeed(double speed) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetPWMSpeed(m_handle, speed, &status);
-  wpi_setHALError(status);
-
-  Feed();
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 double PWM::GetSpeed() const {
-  if (StatusIsFatal()) return 0.0;
   int32_t status = 0;
   double speed = HAL_GetPWMSpeed(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   return speed;
 }
 
 void PWM::SetDisabled() {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
-
   HAL_SetPWMDisabled(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void PWM::SetPeriodMultiplier(PeriodMultiplier mult) {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
 
   switch (mult) {
@@ -145,44 +116,39 @@
       HAL_SetPWMPeriodScale(m_handle, 0, &status);  // Don't squelch any outputs
       break;
     default:
-      wpi_assert(false);
+      throw FRC_MakeError(err::InvalidParameter, "PeriodMultiplier value {}",
+                          mult);
   }
 
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void PWM::SetZeroLatch() {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
-
   HAL_LatchPWMZero(m_handle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void PWM::EnableDeadbandElimination(bool eliminateDeadband) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetPWMEliminateDeadband(m_handle, eliminateDeadband, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void PWM::SetBounds(double max, double deadbandMax, double center,
                     double deadbandMin, double min) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetPWMConfig(m_handle, max, deadbandMax, center, deadbandMin, min,
                    &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void PWM::SetRawBounds(int max, int deadbandMax, int center, int deadbandMin,
                        int min) {
-  if (StatusIsFatal()) return;
   int32_t status = 0;
   HAL_SetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min,
                       &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 void PWM::GetRawBounds(int* max, int* deadbandMax, int* center,
@@ -190,16 +156,17 @@
   int32_t status = 0;
   HAL_GetPWMConfigRaw(m_handle, max, deadbandMax, center, deadbandMin, min,
                       &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
-int PWM::GetChannel() const { return m_channel; }
+int PWM::GetChannel() const {
+  return m_channel;
+}
 
-void PWM::InitSendable(SendableBuilder& builder) {
+void PWM::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("PWM");
   builder.SetActuator(true);
-  builder.SetSafeState([=]() { SetDisabled(); });
+  builder.SetSafeState([=] { SetDisabled(); });
   builder.AddDoubleProperty(
-      "Value", [=]() { return GetRaw(); },
-      [=](double value) { SetRaw(value); });
+      "Value", [=] { return GetRaw(); }, [=](double value) { SetRaw(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/PWMSparkMax.cpp b/wpilibc/src/main/native/cpp/PWMSparkMax.cpp
deleted file mode 100644
index c5375f4..0000000
--- a/wpilibc/src/main/native/cpp/PWMSparkMax.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PWMSparkMax.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-PWMSparkMax::PWMSparkMax(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.003, 1.55, 1.50, 1.46, 0.999);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_RevSparkMaxPWM, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "PWMSparkMax", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/PWMSpeedController.cpp b/wpilibc/src/main/native/cpp/PWMSpeedController.cpp
deleted file mode 100644
index ec0be7b..0000000
--- a/wpilibc/src/main/native/cpp/PWMSpeedController.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PWMSpeedController.h"
-
-#include "frc/smartdashboard/SendableBuilder.h"
-
-using namespace frc;
-
-void PWMSpeedController::Set(double speed) {
-  SetSpeed(m_isInverted ? -speed : speed);
-}
-
-double PWMSpeedController::Get() const { return GetSpeed(); }
-
-void PWMSpeedController::SetInverted(bool isInverted) {
-  m_isInverted = isInverted;
-}
-
-bool PWMSpeedController::GetInverted() const { return m_isInverted; }
-
-void PWMSpeedController::Disable() { SetDisabled(); }
-
-void PWMSpeedController::StopMotor() { PWM::StopMotor(); }
-
-void PWMSpeedController::PIDWrite(double output) { Set(output); }
-
-PWMSpeedController::PWMSpeedController(int channel) : PWM(channel) {}
-
-void PWMSpeedController::InitSendable(SendableBuilder& builder) {
-  builder.SetSmartDashboardType("Speed Controller");
-  builder.SetActuator(true);
-  builder.SetSafeState([=]() { SetDisabled(); });
-  builder.AddDoubleProperty(
-      "Value", [=]() { return GetSpeed(); },
-      [=](double value) { SetSpeed(value); });
-}
diff --git a/wpilibc/src/main/native/cpp/PWMTalonFX.cpp b/wpilibc/src/main/native/cpp/PWMTalonFX.cpp
deleted file mode 100644
index 09b7163..0000000
--- a/wpilibc/src/main/native/cpp/PWMTalonFX.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PWMTalonFX.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-PWMTalonFX::PWMTalonFX(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_TalonFX, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "PWMTalonFX", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp b/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp
deleted file mode 100644
index 1242be9..0000000
--- a/wpilibc/src/main/native/cpp/PWMTalonSRX.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PWMTalonSRX.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-PWMTalonSRX::PWMTalonSRX(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_PWMTalonSRX, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "PWMTalonSRX", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/PWMVenom.cpp b/wpilibc/src/main/native/cpp/PWMVenom.cpp
deleted file mode 100644
index 9fa14b7..0000000
--- a/wpilibc/src/main/native/cpp/PWMVenom.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PWMVenom.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-PWMVenom::PWMVenom(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_FusionVenom, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "PWMVenom", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp b/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp
deleted file mode 100644
index 0d966ae..0000000
--- a/wpilibc/src/main/native/cpp/PWMVictorSPX.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PWMVictorSPX.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-PWMVictorSPX::PWMVictorSPX(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_PWMVictorSPX, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "PWMVictorSPX", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/PneumaticHub.cpp b/wpilibc/src/main/native/cpp/PneumaticHub.cpp
new file mode 100644
index 0000000..8217e86
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/PneumaticHub.cpp
@@ -0,0 +1,209 @@
+// 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.
+
+#include "frc/PneumaticHub.h"
+
+#include <hal/REVPH.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/StackTrace.h>
+
+#include "frc/Compressor.h"
+#include "frc/DoubleSolenoid.h"
+#include "frc/Errors.h"
+#include "frc/SensorUtil.h"
+#include "frc/Solenoid.h"
+
+using namespace frc;
+
+wpi::mutex PneumaticHub::m_handleLock;
+std::unique_ptr<wpi::DenseMap<int, std::weak_ptr<PneumaticHub::DataStore>>>
+    PneumaticHub::m_handleMap = nullptr;
+
+// Always called under lock, so we can avoid the double lock from the magic
+// static
+std::weak_ptr<PneumaticHub::DataStore>& PneumaticHub::GetDataStore(int module) {
+  if (!m_handleMap) {
+    m_handleMap = std::make_unique<
+        wpi::DenseMap<int, std::weak_ptr<PneumaticHub::DataStore>>>();
+  }
+  return (*m_handleMap)[module];
+}
+
+class PneumaticHub::DataStore {
+ public:
+  explicit DataStore(int module, const char* stackTrace) {
+    int32_t status = 0;
+    HAL_REVPHHandle handle = HAL_InitializeREVPH(module, stackTrace, &status);
+    FRC_CheckErrorStatus(status, "Module {}", module);
+    m_moduleObject = PneumaticHub{handle, module};
+    m_moduleObject.m_dataStore =
+        std::shared_ptr<DataStore>{this, wpi::NullDeleter<DataStore>()};
+  }
+
+  ~DataStore() noexcept { HAL_FreeREVPH(m_moduleObject.m_handle); }
+
+  DataStore(DataStore&&) = delete;
+  DataStore& operator=(DataStore&&) = delete;
+
+ private:
+  friend class PneumaticHub;
+  uint32_t m_reservedMask{0};
+  bool m_compressorReserved{false};
+  wpi::mutex m_reservedLock;
+  PneumaticHub m_moduleObject{HAL_kInvalidHandle, 0};
+};
+
+PneumaticHub::PneumaticHub()
+    : PneumaticHub{SensorUtil::GetDefaultREVPHModule()} {}
+
+PneumaticHub::PneumaticHub(int module) {
+  std::string stackTrace = wpi::GetStackTrace(1);
+  std::scoped_lock lock(m_handleLock);
+  auto& res = GetDataStore(module);
+  m_dataStore = res.lock();
+  if (!m_dataStore) {
+    m_dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
+    res = m_dataStore;
+  }
+  m_handle = m_dataStore->m_moduleObject.m_handle;
+  m_module = module;
+}
+
+PneumaticHub::PneumaticHub(HAL_REVPHHandle handle, int module)
+    : m_handle{handle}, m_module{module} {}
+
+bool PneumaticHub::GetCompressor() const {
+  int32_t status = 0;
+  auto result = HAL_GetREVPHCompressor(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+void PneumaticHub::SetClosedLoopControl(bool enabled) {
+  int32_t status = 0;
+  HAL_SetREVPHClosedLoopControl(m_handle, enabled, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+bool PneumaticHub::GetClosedLoopControl() const {
+  int32_t status = 0;
+  auto result = HAL_GetREVPHClosedLoopControl(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+bool PneumaticHub::GetPressureSwitch() const {
+  int32_t status = 0;
+  auto result = HAL_GetREVPHPressureSwitch(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+double PneumaticHub::GetCompressorCurrent() const {
+  int32_t status = 0;
+  auto result = HAL_GetREVPHCompressorCurrent(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+void PneumaticHub::SetSolenoids(int mask, int values) {
+  int32_t status = 0;
+  HAL_SetREVPHSolenoids(m_handle, mask, values, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+int PneumaticHub::GetSolenoids() const {
+  int32_t status = 0;
+  auto result = HAL_GetREVPHSolenoids(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+int PneumaticHub::GetModuleNumber() const {
+  return m_module;
+}
+
+int PneumaticHub::GetSolenoidDisabledList() const {
+  return 0;
+  // TODO Fix me
+  // int32_t status = 0;
+  // auto result = HAL_GetREVPHSolenoidDisabledList(m_handle, &status);
+  // FRC_CheckErrorStatus(status, "Module {}", m_module);
+  // return result;
+}
+
+void PneumaticHub::FireOneShot(int index) {
+  // TODO Fix me
+  // int32_t status = 0;
+  // HAL_FireREVPHOneShot(m_handle, index, &status);
+  // FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+void PneumaticHub::SetOneShotDuration(int index, units::second_t duration) {
+  // TODO Fix me
+  // int32_t status = 0;
+  // units::millisecond_t millis = duration;
+  // HAL_SetREVPHOneShotDuration(m_handle, index, millis.to<int32_t>(),
+  // &status); FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+bool PneumaticHub::CheckSolenoidChannel(int channel) const {
+  return HAL_CheckREVPHSolenoidChannel(channel);
+}
+
+int PneumaticHub::CheckAndReserveSolenoids(int mask) {
+  std::scoped_lock lock{m_dataStore->m_reservedLock};
+  uint32_t uMask = static_cast<uint32_t>(mask);
+  if ((m_dataStore->m_reservedMask & uMask) != 0) {
+    return m_dataStore->m_reservedMask & uMask;
+  }
+  m_dataStore->m_reservedMask |= uMask;
+  return 0;
+}
+
+void PneumaticHub::UnreserveSolenoids(int mask) {
+  std::scoped_lock lock{m_dataStore->m_reservedLock};
+  m_dataStore->m_reservedMask &= ~(static_cast<uint32_t>(mask));
+}
+
+bool PneumaticHub::ReserveCompressor() {
+  std::scoped_lock lock{m_dataStore->m_reservedLock};
+  if (m_dataStore->m_compressorReserved) {
+    return false;
+  }
+  m_dataStore->m_compressorReserved = true;
+  return true;
+}
+
+void PneumaticHub::UnreserveCompressor() {
+  std::scoped_lock lock{m_dataStore->m_reservedLock};
+  m_dataStore->m_compressorReserved = false;
+}
+
+Solenoid PneumaticHub::MakeSolenoid(int channel) {
+  return Solenoid{m_module, PneumaticsModuleType::REVPH, channel};
+}
+
+DoubleSolenoid PneumaticHub::MakeDoubleSolenoid(int forwardChannel,
+                                                int reverseChannel) {
+  return DoubleSolenoid{m_module, PneumaticsModuleType::REVPH, forwardChannel,
+                        reverseChannel};
+}
+
+Compressor PneumaticHub::MakeCompressor() {
+  return Compressor{m_module, PneumaticsModuleType::REVPH};
+}
+
+std::shared_ptr<PneumaticsBase> PneumaticHub::GetForModule(int module) {
+  std::string stackTrace = wpi::GetStackTrace(1);
+  std::scoped_lock lock(m_handleLock);
+  auto& res = GetDataStore(module);
+  std::shared_ptr<DataStore> dataStore = res.lock();
+  if (!dataStore) {
+    dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
+    res = dataStore;
+  }
+
+  return std::shared_ptr<PneumaticsBase>{dataStore, &dataStore->m_moduleObject};
+}
diff --git a/wpilibc/src/main/native/cpp/PneumaticsBase.cpp b/wpilibc/src/main/native/cpp/PneumaticsBase.cpp
new file mode 100644
index 0000000..9d96651
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/PneumaticsBase.cpp
@@ -0,0 +1,31 @@
+// 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.
+
+#include "frc/PneumaticsBase.h"
+
+#include "frc/Errors.h"
+#include "frc/PneumaticHub.h"
+#include "frc/PneumaticsControlModule.h"
+#include "frc/SensorUtil.h"
+
+using namespace frc;
+
+std::shared_ptr<PneumaticsBase> PneumaticsBase::GetForType(
+    int module, PneumaticsModuleType moduleType) {
+  if (moduleType == PneumaticsModuleType::CTREPCM) {
+    return PneumaticsControlModule::GetForModule(module);
+  } else if (moduleType == PneumaticsModuleType::REVPH) {
+    return PneumaticHub::GetForModule(module);
+  }
+  throw FRC_MakeError(err::InvalidParameter, "{}", moduleType);
+}
+
+int PneumaticsBase::GetDefaultForType(PneumaticsModuleType moduleType) {
+  if (moduleType == PneumaticsModuleType::CTREPCM) {
+    return SensorUtil::GetDefaultCTREPCMModule();
+  } else if (moduleType == PneumaticsModuleType::REVPH) {
+    return SensorUtil::GetDefaultREVPHModule();
+  }
+  throw FRC_MakeError(err::InvalidParameter, "{}", moduleType);
+}
diff --git a/wpilibc/src/main/native/cpp/PneumaticsControlModule.cpp b/wpilibc/src/main/native/cpp/PneumaticsControlModule.cpp
new file mode 100644
index 0000000..32de5ca
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/PneumaticsControlModule.cpp
@@ -0,0 +1,269 @@
+// 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.
+
+#include "frc/PneumaticsControlModule.h"
+
+#include <hal/CTREPCM.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/StackTrace.h>
+
+#include "frc/Compressor.h"
+#include "frc/DoubleSolenoid.h"
+#include "frc/Errors.h"
+#include "frc/SensorUtil.h"
+#include "frc/Solenoid.h"
+
+using namespace frc;
+
+wpi::mutex PneumaticsControlModule::m_handleLock;
+std::unique_ptr<
+    wpi::DenseMap<int, std::weak_ptr<PneumaticsControlModule::DataStore>>>
+    PneumaticsControlModule::m_handleMap = nullptr;
+
+// Always called under lock, so we can avoid the double lock from the magic
+// static
+std::weak_ptr<PneumaticsControlModule::DataStore>&
+PneumaticsControlModule::GetDataStore(int module) {
+  if (!m_handleMap) {
+    m_handleMap = std::make_unique<wpi::DenseMap<
+        int, std::weak_ptr<PneumaticsControlModule::DataStore>>>();
+  }
+  return (*m_handleMap)[module];
+}
+
+class PneumaticsControlModule::DataStore {
+ public:
+  explicit DataStore(int module, const char* stackTrace) {
+    int32_t status = 0;
+    HAL_CTREPCMHandle handle =
+        HAL_InitializeCTREPCM(module, stackTrace, &status);
+    FRC_CheckErrorStatus(status, "Module {}", module);
+    m_moduleObject = PneumaticsControlModule{handle, module};
+    m_moduleObject.m_dataStore =
+        std::shared_ptr<DataStore>{this, wpi::NullDeleter<DataStore>()};
+  }
+
+  ~DataStore() noexcept { HAL_FreeCTREPCM(m_moduleObject.m_handle); }
+
+  DataStore(DataStore&&) = delete;
+  DataStore& operator=(DataStore&&) = delete;
+
+ private:
+  friend class PneumaticsControlModule;
+  uint32_t m_reservedMask{0};
+  bool m_compressorReserved{false};
+  wpi::mutex m_reservedLock;
+  PneumaticsControlModule m_moduleObject{HAL_kInvalidHandle, 0};
+};
+
+PneumaticsControlModule::PneumaticsControlModule()
+    : PneumaticsControlModule{SensorUtil::GetDefaultCTREPCMModule()} {}
+
+PneumaticsControlModule::PneumaticsControlModule(int module) {
+  std::string stackTrace = wpi::GetStackTrace(1);
+  std::scoped_lock lock(m_handleLock);
+  auto& res = GetDataStore(module);
+  m_dataStore = res.lock();
+  if (!m_dataStore) {
+    m_dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
+    res = m_dataStore;
+  }
+  m_handle = m_dataStore->m_moduleObject.m_handle;
+  m_module = module;
+}
+
+PneumaticsControlModule::PneumaticsControlModule(HAL_CTREPCMHandle handle,
+                                                 int module)
+    : m_handle{handle}, m_module{module} {}
+
+bool PneumaticsControlModule::GetCompressor() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMCompressor(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+void PneumaticsControlModule::SetClosedLoopControl(bool enabled) {
+  int32_t status = 0;
+  HAL_SetCTREPCMClosedLoopControl(m_handle, enabled, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+bool PneumaticsControlModule::GetClosedLoopControl() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMClosedLoopControl(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+bool PneumaticsControlModule::GetPressureSwitch() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMPressureSwitch(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+double PneumaticsControlModule::GetCompressorCurrent() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMCompressorCurrent(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+bool PneumaticsControlModule::GetCompressorCurrentTooHighFault() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMCompressorCurrentTooHighFault(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+bool PneumaticsControlModule::GetCompressorCurrentTooHighStickyFault() const {
+  int32_t status = 0;
+  auto result =
+      HAL_GetCTREPCMCompressorCurrentTooHighStickyFault(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+bool PneumaticsControlModule::GetCompressorShortedFault() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMCompressorShortedFault(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+bool PneumaticsControlModule::GetCompressorShortedStickyFault() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMCompressorShortedStickyFault(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+bool PneumaticsControlModule::GetCompressorNotConnectedFault() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMCompressorNotConnectedFault(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+bool PneumaticsControlModule::GetCompressorNotConnectedStickyFault() const {
+  int32_t status = 0;
+  auto result =
+      HAL_GetCTREPCMCompressorNotConnectedStickyFault(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+bool PneumaticsControlModule::GetSolenoidVoltageFault() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMSolenoidVoltageFault(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+bool PneumaticsControlModule::GetSolenoidVoltageStickyFault() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMSolenoidVoltageStickyFault(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+void PneumaticsControlModule::ClearAllStickyFaults() {
+  int32_t status = 0;
+  HAL_ClearAllCTREPCMStickyFaults(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+void PneumaticsControlModule::SetSolenoids(int mask, int values) {
+  int32_t status = 0;
+  HAL_SetCTREPCMSolenoids(m_handle, mask, values, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+int PneumaticsControlModule::GetSolenoids() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMSolenoids(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+int PneumaticsControlModule::GetModuleNumber() const {
+  return m_module;
+}
+
+int PneumaticsControlModule::GetSolenoidDisabledList() const {
+  int32_t status = 0;
+  auto result = HAL_GetCTREPCMSolenoidDisabledList(m_handle, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+  return result;
+}
+
+void PneumaticsControlModule::FireOneShot(int index) {
+  int32_t status = 0;
+  HAL_FireCTREPCMOneShot(m_handle, index, &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+void PneumaticsControlModule::SetOneShotDuration(int index,
+                                                 units::second_t duration) {
+  int32_t status = 0;
+  units::millisecond_t millis = duration;
+  HAL_SetCTREPCMOneShotDuration(m_handle, index, millis.to<int32_t>(), &status);
+  FRC_CheckErrorStatus(status, "Module {}", m_module);
+}
+
+bool PneumaticsControlModule::CheckSolenoidChannel(int channel) const {
+  return HAL_CheckCTREPCMSolenoidChannel(channel);
+}
+
+int PneumaticsControlModule::CheckAndReserveSolenoids(int mask) {
+  std::scoped_lock lock{m_dataStore->m_reservedLock};
+  uint32_t uMask = static_cast<uint32_t>(mask);
+  if ((m_dataStore->m_reservedMask & uMask) != 0) {
+    return m_dataStore->m_reservedMask & uMask;
+  }
+  m_dataStore->m_reservedMask |= uMask;
+  return 0;
+}
+
+void PneumaticsControlModule::UnreserveSolenoids(int mask) {
+  std::scoped_lock lock{m_dataStore->m_reservedLock};
+  m_dataStore->m_reservedMask &= ~(static_cast<uint32_t>(mask));
+}
+
+bool PneumaticsControlModule::ReserveCompressor() {
+  std::scoped_lock lock{m_dataStore->m_reservedLock};
+  if (m_dataStore->m_compressorReserved) {
+    return false;
+  }
+  m_dataStore->m_compressorReserved = true;
+  return true;
+}
+
+void PneumaticsControlModule::UnreserveCompressor() {
+  std::scoped_lock lock{m_dataStore->m_reservedLock};
+  m_dataStore->m_compressorReserved = false;
+}
+
+Solenoid PneumaticsControlModule::MakeSolenoid(int channel) {
+  return Solenoid{m_module, PneumaticsModuleType::CTREPCM, channel};
+}
+
+DoubleSolenoid PneumaticsControlModule::MakeDoubleSolenoid(int forwardChannel,
+                                                           int reverseChannel) {
+  return DoubleSolenoid{m_module, PneumaticsModuleType::CTREPCM, forwardChannel,
+                        reverseChannel};
+}
+
+Compressor PneumaticsControlModule::MakeCompressor() {
+  return Compressor{m_module, PneumaticsModuleType::CTREPCM};
+}
+
+std::shared_ptr<PneumaticsBase> PneumaticsControlModule::GetForModule(
+    int module) {
+  std::string stackTrace = wpi::GetStackTrace(1);
+  std::scoped_lock lock(m_handleLock);
+  auto& res = GetDataStore(module);
+  std::shared_ptr<DataStore> dataStore = res.lock();
+  if (!dataStore) {
+    dataStore = std::make_shared<DataStore>(module, stackTrace.c_str());
+    res = dataStore;
+  }
+
+  return std::shared_ptr<PneumaticsBase>{dataStore, &dataStore->m_moduleObject};
+}
diff --git a/wpilibc/src/main/native/cpp/PowerDistribution.cpp b/wpilibc/src/main/native/cpp/PowerDistribution.cpp
new file mode 100644
index 0000000..4c2acfc
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/PowerDistribution.cpp
@@ -0,0 +1,146 @@
+// 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.
+
+#include "frc/PowerDistribution.h"
+
+#include <fmt/format.h>
+#include <hal/FRCUsageReporting.h>
+#include <hal/Ports.h>
+#include <hal/PowerDistribution.h>
+#include <wpi/StackTrace.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
+
+#include "frc/Errors.h"
+#include "frc/SensorUtil.h"
+
+static_assert(static_cast<HAL_PowerDistributionType>(
+                  frc::PowerDistribution::ModuleType::kAutomatic) ==
+              HAL_PowerDistributionType::HAL_PowerDistributionType_kAutomatic);
+static_assert(static_cast<HAL_PowerDistributionType>(
+                  frc::PowerDistribution::ModuleType::kCTRE) ==
+              HAL_PowerDistributionType::HAL_PowerDistributionType_kCTRE);
+static_assert(static_cast<HAL_PowerDistributionType>(
+                  frc::PowerDistribution::ModuleType::kRev) ==
+              HAL_PowerDistributionType::HAL_PowerDistributionType_kRev);
+static_assert(frc::PowerDistribution::kDefaultModule ==
+              HAL_DEFAULT_POWER_DISTRIBUTION_MODULE);
+
+using namespace frc;
+
+PowerDistribution::PowerDistribution()
+    : PowerDistribution(-1, ModuleType::kAutomatic) {}
+
+PowerDistribution::PowerDistribution(int module, ModuleType moduleType) {
+  auto stack = wpi::GetStackTrace(1);
+
+  int32_t status = 0;
+  m_handle = HAL_InitializePowerDistribution(
+      module, static_cast<HAL_PowerDistributionType>(moduleType), stack.c_str(),
+      &status);
+  FRC_CheckErrorStatus(status, "Module {}", module);
+  m_module = HAL_GetPowerDistributionModuleNumber(m_handle, &status);
+  FRC_ReportError(status, "Module {}", module);
+
+  HAL_Report(HALUsageReporting::kResourceType_PDP, m_module + 1);
+  wpi::SendableRegistry::AddLW(this, "PowerDistribution", m_module);
+}
+
+PowerDistribution::~PowerDistribution() {
+  if (m_handle != HAL_kInvalidHandle) {
+    HAL_CleanPowerDistribution(m_handle);
+    m_handle = HAL_kInvalidHandle;
+  }
+}
+
+double PowerDistribution::GetVoltage() const {
+  int32_t status = 0;
+  double voltage = HAL_GetPowerDistributionVoltage(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+  return voltage;
+}
+
+double PowerDistribution::GetTemperature() const {
+  int32_t status = 0;
+  double temperature = HAL_GetPowerDistributionTemperature(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+  return temperature;
+}
+
+double PowerDistribution::GetCurrent(int channel) const {
+  int32_t status = 0;
+  double current =
+      HAL_GetPowerDistributionChannelCurrent(m_handle, channel, &status);
+  FRC_ReportError(status, "Module {} Channel {}", m_module, channel);
+
+  return current;
+}
+
+double PowerDistribution::GetTotalCurrent() const {
+  int32_t status = 0;
+  double current = HAL_GetPowerDistributionTotalCurrent(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+  return current;
+}
+
+double PowerDistribution::GetTotalPower() const {
+  int32_t status = 0;
+  double power = HAL_GetPowerDistributionTotalPower(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+  return power;
+}
+
+double PowerDistribution::GetTotalEnergy() const {
+  int32_t status = 0;
+  double energy = HAL_GetPowerDistributionTotalEnergy(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+  return energy;
+}
+
+void PowerDistribution::ResetTotalEnergy() {
+  int32_t status = 0;
+  HAL_ResetPowerDistributionTotalEnergy(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+}
+
+void PowerDistribution::ClearStickyFaults() {
+  int32_t status = 0;
+  HAL_ClearPowerDistributionStickyFaults(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+}
+
+int PowerDistribution::GetModule() const {
+  return m_module;
+}
+
+bool PowerDistribution::GetSwitchableChannel() const {
+  int32_t status = 0;
+  bool state = HAL_GetPowerDistributionSwitchableChannel(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+  return state;
+}
+
+void PowerDistribution::SetSwitchableChannel(bool enabled) {
+  int32_t status = 0;
+  HAL_SetPowerDistributionSwitchableChannel(m_handle, enabled, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+}
+
+void PowerDistribution::InitSendable(wpi::SendableBuilder& builder) {
+  builder.SetSmartDashboardType("PowerDistribution");
+  int32_t status = 0;
+  int numChannels = HAL_GetPowerDistributionNumChannels(m_handle, &status);
+  FRC_ReportError(status, "Module {}", m_module);
+  for (int i = 0; i < numChannels; ++i) {
+    builder.AddDoubleProperty(
+        fmt::format("Chan{}", i), [=] { return GetCurrent(i); }, nullptr);
+  }
+  builder.AddDoubleProperty(
+      "Voltage", [=] { return GetVoltage(); }, nullptr);
+  builder.AddDoubleProperty(
+      "TotalCurrent", [=] { return GetTotalCurrent(); }, nullptr);
+  builder.AddBooleanProperty(
+      "SwitchableChannel", [=] { return GetSwitchableChannel(); },
+      [=](bool value) { SetSwitchableChannel(value); });
+}
diff --git a/wpilibc/src/main/native/cpp/PowerDistributionPanel.cpp b/wpilibc/src/main/native/cpp/PowerDistributionPanel.cpp
deleted file mode 100644
index 4070633..0000000
--- a/wpilibc/src/main/native/cpp/PowerDistributionPanel.cpp
+++ /dev/null
@@ -1,151 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2014-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/PowerDistributionPanel.h"
-
-#include <hal/FRCUsageReporting.h>
-#include <hal/PDP.h>
-#include <hal/Ports.h>
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
-
-#include "frc/SensorUtil.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-PowerDistributionPanel::PowerDistributionPanel() : PowerDistributionPanel(0) {}
-
-/**
- * Initialize the PDP.
- */
-PowerDistributionPanel::PowerDistributionPanel(int module) : m_module(module) {
-  int32_t status = 0;
-  m_handle = HAL_InitializePDP(module, &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumPDPModules(), module);
-    return;
-  }
-
-  HAL_Report(HALUsageReporting::kResourceType_PDP, module + 1);
-  SendableRegistry::GetInstance().AddLW(this, "PowerDistributionPanel", module);
-}
-
-double PowerDistributionPanel::GetVoltage() const {
-  int32_t status = 0;
-
-  double voltage = HAL_GetPDPVoltage(m_handle, &status);
-
-  if (status) {
-    wpi_setWPIErrorWithContext(Timeout, "");
-  }
-
-  return voltage;
-}
-
-double PowerDistributionPanel::GetTemperature() const {
-  int32_t status = 0;
-
-  double temperature = HAL_GetPDPTemperature(m_handle, &status);
-
-  if (status) {
-    wpi_setWPIErrorWithContext(Timeout, "");
-  }
-
-  return temperature;
-}
-
-double PowerDistributionPanel::GetCurrent(int channel) const {
-  int32_t status = 0;
-
-  if (!SensorUtil::CheckPDPChannel(channel)) {
-    wpi::SmallString<32> str;
-    wpi::raw_svector_ostream buf(str);
-    buf << "PDP Channel " << channel;
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, buf.str());
-  }
-
-  double current = HAL_GetPDPChannelCurrent(m_handle, channel, &status);
-
-  if (status) {
-    wpi_setWPIErrorWithContext(Timeout, "");
-  }
-
-  return current;
-}
-
-double PowerDistributionPanel::GetTotalCurrent() const {
-  int32_t status = 0;
-
-  double current = HAL_GetPDPTotalCurrent(m_handle, &status);
-
-  if (status) {
-    wpi_setWPIErrorWithContext(Timeout, "");
-  }
-
-  return current;
-}
-
-double PowerDistributionPanel::GetTotalPower() const {
-  int32_t status = 0;
-
-  double power = HAL_GetPDPTotalPower(m_handle, &status);
-
-  if (status) {
-    wpi_setWPIErrorWithContext(Timeout, "");
-  }
-
-  return power;
-}
-
-double PowerDistributionPanel::GetTotalEnergy() const {
-  int32_t status = 0;
-
-  double energy = HAL_GetPDPTotalEnergy(m_handle, &status);
-
-  if (status) {
-    wpi_setWPIErrorWithContext(Timeout, "");
-  }
-
-  return energy;
-}
-
-void PowerDistributionPanel::ResetTotalEnergy() {
-  int32_t status = 0;
-
-  HAL_ResetPDPTotalEnergy(m_handle, &status);
-
-  if (status) {
-    wpi_setWPIErrorWithContext(Timeout, "");
-  }
-}
-
-void PowerDistributionPanel::ClearStickyFaults() {
-  int32_t status = 0;
-
-  HAL_ClearPDPStickyFaults(m_handle, &status);
-
-  if (status) {
-    wpi_setWPIErrorWithContext(Timeout, "");
-  }
-}
-
-int PowerDistributionPanel::GetModule() const { return m_module; }
-
-void PowerDistributionPanel::InitSendable(SendableBuilder& builder) {
-  builder.SetSmartDashboardType("PowerDistributionPanel");
-  for (int i = 0; i < SensorUtil::kPDPChannels; ++i) {
-    builder.AddDoubleProperty(
-        "Chan" + wpi::Twine(i), [=]() { return GetCurrent(i); }, nullptr);
-  }
-  builder.AddDoubleProperty(
-      "Voltage", [=]() { return GetVoltage(); }, nullptr);
-  builder.AddDoubleProperty(
-      "TotalCurrent", [=]() { return GetTotalCurrent(); }, nullptr);
-}
diff --git a/wpilibc/src/main/native/cpp/Preferences.cpp b/wpilibc/src/main/native/cpp/Preferences.cpp
index 8c1eb87..0c82f54 100644
--- a/wpilibc/src/main/native/cpp/Preferences.cpp
+++ b/wpilibc/src/main/native/cpp/Preferences.cpp
@@ -1,128 +1,168 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2011-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Preferences.h"
 
 #include <algorithm>
 
 #include <hal/FRCUsageReporting.h>
+#include <networktables/NetworkTable.h>
 #include <networktables/NetworkTableInstance.h>
-#include <wpi/StringRef.h>
-
-#include "frc/WPIErrors.h"
 
 using namespace frc;
 
 // The Preferences table name
-static wpi::StringRef kTableName{"Preferences"};
+static constexpr std::string_view kTableName{"Preferences"};
+
+namespace {
+struct Instance {
+  Instance();
+
+  std::shared_ptr<nt::NetworkTable> table{
+      nt::NetworkTableInstance::GetDefault().GetTable(kTableName)};
+  NT_EntryListener listener;
+};
+}  // namespace
+
+static Instance& GetInstance() {
+  static Instance instance;
+  return instance;
+}
 
 Preferences* Preferences::GetInstance() {
+  ::GetInstance();
   static Preferences instance;
   return &instance;
 }
 
-std::vector<std::string> Preferences::GetKeys() { return m_table->GetKeys(); }
-
-std::string Preferences::GetString(wpi::StringRef key,
-                                   wpi::StringRef defaultValue) {
-  return m_table->GetString(key, defaultValue);
+std::vector<std::string> Preferences::GetKeys() {
+  return ::GetInstance().table->GetKeys();
 }
 
-int Preferences::GetInt(wpi::StringRef key, int defaultValue) {
-  return static_cast<int>(m_table->GetNumber(key, defaultValue));
+std::string Preferences::GetString(std::string_view key,
+                                   std::string_view defaultValue) {
+  return ::GetInstance().table->GetString(key, defaultValue);
 }
 
-double Preferences::GetDouble(wpi::StringRef key, double defaultValue) {
-  return m_table->GetNumber(key, defaultValue);
+int Preferences::GetInt(std::string_view key, int defaultValue) {
+  return static_cast<int>(::GetInstance().table->GetNumber(key, defaultValue));
 }
 
-float Preferences::GetFloat(wpi::StringRef key, float defaultValue) {
-  return m_table->GetNumber(key, defaultValue);
+double Preferences::GetDouble(std::string_view key, double defaultValue) {
+  return ::GetInstance().table->GetNumber(key, defaultValue);
 }
 
-bool Preferences::GetBoolean(wpi::StringRef key, bool defaultValue) {
-  return m_table->GetBoolean(key, defaultValue);
+float Preferences::GetFloat(std::string_view key, float defaultValue) {
+  return ::GetInstance().table->GetNumber(key, defaultValue);
 }
 
-int64_t Preferences::GetLong(wpi::StringRef key, int64_t defaultValue) {
-  return static_cast<int64_t>(m_table->GetNumber(key, defaultValue));
+bool Preferences::GetBoolean(std::string_view key, bool defaultValue) {
+  return ::GetInstance().table->GetBoolean(key, defaultValue);
 }
 
-void Preferences::PutString(wpi::StringRef key, wpi::StringRef value) {
-  auto entry = m_table->GetEntry(key);
+int64_t Preferences::GetLong(std::string_view key, int64_t defaultValue) {
+  return static_cast<int64_t>(
+      ::GetInstance().table->GetNumber(key, defaultValue));
+}
+
+void Preferences::SetString(std::string_view key, std::string_view value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetString(value);
   entry.SetPersistent();
 }
 
-void Preferences::InitString(wpi::StringRef key, wpi::StringRef value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::PutString(std::string_view key, std::string_view value) {
+  SetString(key, value);
+}
+
+void Preferences::InitString(std::string_view key, std::string_view value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDefaultString(value);
 }
 
-void Preferences::PutInt(wpi::StringRef key, int value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::SetInt(std::string_view key, int value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDouble(value);
   entry.SetPersistent();
 }
 
-void Preferences::InitInt(wpi::StringRef key, int value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::PutInt(std::string_view key, int value) {
+  SetInt(key, value);
+}
+
+void Preferences::InitInt(std::string_view key, int value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDefaultDouble(value);
 }
 
-void Preferences::PutDouble(wpi::StringRef key, double value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::SetDouble(std::string_view key, double value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDouble(value);
   entry.SetPersistent();
 }
 
-void Preferences::InitDouble(wpi::StringRef key, double value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::PutDouble(std::string_view key, double value) {
+  SetDouble(key, value);
+}
+
+void Preferences::InitDouble(std::string_view key, double value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDefaultDouble(value);
 }
 
-void Preferences::PutFloat(wpi::StringRef key, float value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::SetFloat(std::string_view key, float value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDouble(value);
   entry.SetPersistent();
 }
 
-void Preferences::InitFloat(wpi::StringRef key, float value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::PutFloat(std::string_view key, float value) {
+  SetFloat(key, value);
+}
+
+void Preferences::InitFloat(std::string_view key, float value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDefaultDouble(value);
 }
 
-void Preferences::PutBoolean(wpi::StringRef key, bool value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::SetBoolean(std::string_view key, bool value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetBoolean(value);
   entry.SetPersistent();
 }
 
-void Preferences::InitBoolean(wpi::StringRef key, bool value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::PutBoolean(std::string_view key, bool value) {
+  SetBoolean(key, value);
+}
+
+void Preferences::InitBoolean(std::string_view key, bool value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDefaultBoolean(value);
 }
 
-void Preferences::PutLong(wpi::StringRef key, int64_t value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::SetLong(std::string_view key, int64_t value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDouble(value);
   entry.SetPersistent();
 }
 
-void Preferences::InitLong(wpi::StringRef key, int64_t value) {
-  auto entry = m_table->GetEntry(key);
+void Preferences::PutLong(std::string_view key, int64_t value) {
+  SetLong(key, value);
+}
+
+void Preferences::InitLong(std::string_view key, int64_t value) {
+  auto entry = ::GetInstance().table->GetEntry(key);
   entry.SetDefaultDouble(value);
 }
 
-bool Preferences::ContainsKey(wpi::StringRef key) {
-  return m_table->ContainsKey(key);
+bool Preferences::ContainsKey(std::string_view key) {
+  return ::GetInstance().table->ContainsKey(key);
 }
 
-void Preferences::Remove(wpi::StringRef key) { m_table->Delete(key); }
+void Preferences::Remove(std::string_view key) {
+  ::GetInstance().table->Delete(key);
+}
 
 void Preferences::RemoveAll() {
   for (auto preference : GetKeys()) {
@@ -132,11 +172,10 @@
   }
 }
 
-Preferences::Preferences()
-    : m_table(nt::NetworkTableInstance::GetDefault().GetTable(kTableName)) {
-  m_table->GetEntry(".type").SetString("RobotPreferences");
-  m_listener = m_table->AddEntryListener(
-      [=](nt::NetworkTable* table, wpi::StringRef name,
+Instance::Instance() {
+  table->GetEntry(".type").SetString("RobotPreferences");
+  listener = table->AddEntryListener(
+      [=](nt::NetworkTable* table, std::string_view name,
           nt::NetworkTableEntry entry, std::shared_ptr<nt::Value> value,
           int flags) { entry.SetPersistent(); },
       NT_NOTIFY_NEW | NT_NOTIFY_IMMEDIATE);
diff --git a/wpilibc/src/main/native/cpp/Relay.cpp b/wpilibc/src/main/native/cpp/Relay.cpp
index cb95223..9bec566 100644
--- a/wpilibc/src/main/native/cpp/Relay.cpp
+++ b/wpilibc/src/main/native/cpp/Relay.cpp
@@ -1,32 +1,29 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Relay.h"
 
 #include <utility>
 
+#include <fmt/format.h>
 #include <hal/FRCUsageReporting.h>
 #include <hal/HALBase.h>
 #include <hal/Ports.h>
 #include <hal/Relay.h>
-#include <wpi/raw_ostream.h>
+#include <wpi/StackTrace.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
 Relay::Relay(int channel, Relay::Direction direction)
     : m_channel(channel), m_direction(direction) {
   if (!SensorUtil::CheckRelayChannel(m_channel)) {
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange,
-                               "Relay Channel " + wpi::Twine(m_channel));
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}", m_channel);
     return;
   }
 
@@ -34,49 +31,32 @@
 
   if (m_direction == kBothDirections || m_direction == kForwardOnly) {
     int32_t status = 0;
-    m_forwardHandle = HAL_InitializeRelayPort(portHandle, true, &status);
-    if (status != 0) {
-      wpi_setHALErrorWithRange(status, 0, HAL_GetNumRelayChannels(), channel);
-      m_forwardHandle = HAL_kInvalidHandle;
-      m_reverseHandle = HAL_kInvalidHandle;
-      return;
-    }
+    std::string stackTrace = wpi::GetStackTrace(1);
+    m_forwardHandle =
+        HAL_InitializeRelayPort(portHandle, true, stackTrace.c_str(), &status);
+    FRC_CheckErrorStatus(status, "Channel {}", m_channel);
     HAL_Report(HALUsageReporting::kResourceType_Relay, m_channel + 1);
   }
   if (m_direction == kBothDirections || m_direction == kReverseOnly) {
     int32_t status = 0;
-    m_reverseHandle = HAL_InitializeRelayPort(portHandle, false, &status);
-    if (status != 0) {
-      wpi_setHALErrorWithRange(status, 0, HAL_GetNumRelayChannels(), channel);
-      m_forwardHandle = HAL_kInvalidHandle;
-      m_reverseHandle = HAL_kInvalidHandle;
-      return;
-    }
-
+    std::string stackTrace = wpi::GetStackTrace(1);
+    m_reverseHandle =
+        HAL_InitializeRelayPort(portHandle, false, stackTrace.c_str(), &status);
+    FRC_CheckErrorStatus(status, "Channel {}", m_channel);
     HAL_Report(HALUsageReporting::kResourceType_Relay, m_channel + 128);
   }
 
   int32_t status = 0;
   if (m_forwardHandle != HAL_kInvalidHandle) {
     HAL_SetRelay(m_forwardHandle, false, &status);
-    if (status != 0) {
-      wpi_setHALError(status);
-      m_forwardHandle = HAL_kInvalidHandle;
-      m_reverseHandle = HAL_kInvalidHandle;
-      return;
-    }
+    FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   }
   if (m_reverseHandle != HAL_kInvalidHandle) {
     HAL_SetRelay(m_reverseHandle, false, &status);
-    if (status != 0) {
-      wpi_setHALError(status);
-      m_forwardHandle = HAL_kInvalidHandle;
-      m_reverseHandle = HAL_kInvalidHandle;
-      return;
-    }
+    FRC_CheckErrorStatus(status, "Channel {}", m_channel);
   }
 
-  SendableRegistry::GetInstance().AddLW(this, "Relay", m_channel);
+  wpi::SendableRegistry::AddLW(this, "Relay", m_channel);
 }
 
 Relay::~Relay() {
@@ -84,13 +64,15 @@
   HAL_SetRelay(m_forwardHandle, false, &status);
   HAL_SetRelay(m_reverseHandle, false, &status);
   // ignore errors, as we want to make sure a free happens.
-  if (m_forwardHandle != HAL_kInvalidHandle) HAL_FreeRelayPort(m_forwardHandle);
-  if (m_reverseHandle != HAL_kInvalidHandle) HAL_FreeRelayPort(m_reverseHandle);
+  if (m_forwardHandle != HAL_kInvalidHandle) {
+    HAL_FreeRelayPort(m_forwardHandle);
+  }
+  if (m_reverseHandle != HAL_kInvalidHandle) {
+    HAL_FreeRelayPort(m_reverseHandle);
+  }
 }
 
 void Relay::Set(Relay::Value value) {
-  if (StatusIsFatal()) return;
-
   int32_t status = 0;
 
   switch (value) {
@@ -112,7 +94,8 @@
       break;
     case kForward:
       if (m_direction == kReverseOnly) {
-        wpi_setWPIError(IncompatibleMode);
+        FRC_ReportError(err::IncompatibleMode, "channel {} setting {}",
+                        m_channel, "forward");
         break;
       }
       if (m_direction == kBothDirections || m_direction == kForwardOnly) {
@@ -124,7 +107,8 @@
       break;
     case kReverse:
       if (m_direction == kForwardOnly) {
-        wpi_setWPIError(IncompatibleMode);
+        FRC_ReportError(err::IncompatibleMode, "channel {} setting {}",
+                        m_channel, "reverse");
         break;
       }
       if (m_direction == kBothDirections) {
@@ -136,58 +120,65 @@
       break;
   }
 
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
 }
 
 Relay::Value Relay::Get() const {
-  int32_t status;
+  Relay::Value value = kOff;
+  int32_t status = 0;
 
   if (m_direction == kForwardOnly) {
     if (HAL_GetRelay(m_forwardHandle, &status)) {
-      return kOn;
+      value = kOn;
     } else {
-      return kOff;
+      value = kOff;
     }
   } else if (m_direction == kReverseOnly) {
     if (HAL_GetRelay(m_reverseHandle, &status)) {
-      return kOn;
+      value = kOn;
     } else {
-      return kOff;
+      value = kOff;
     }
   } else {
     if (HAL_GetRelay(m_forwardHandle, &status)) {
       if (HAL_GetRelay(m_reverseHandle, &status)) {
-        return kOn;
+        value = kOn;
       } else {
-        return kForward;
+        value = kForward;
       }
     } else {
       if (HAL_GetRelay(m_reverseHandle, &status)) {
-        return kReverse;
+        value = kReverse;
       } else {
-        return kOff;
+        value = kOff;
       }
     }
   }
 
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Channel {}", m_channel);
+
+  return value;
 }
 
-int Relay::GetChannel() const { return m_channel; }
-
-void Relay::StopMotor() { Set(kOff); }
-
-void Relay::GetDescription(wpi::raw_ostream& desc) const {
-  desc << "Relay " << GetChannel();
+int Relay::GetChannel() const {
+  return m_channel;
 }
 
-void Relay::InitSendable(SendableBuilder& builder) {
+void Relay::StopMotor() {
+  Set(kOff);
+}
+
+std::string Relay::GetDescription() const {
+  return fmt::format("Relay {}", GetChannel());
+}
+
+void Relay::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Relay");
   builder.SetActuator(true);
-  builder.SetSafeState([=]() { Set(kOff); });
+  builder.SetSafeState([=] { Set(kOff); });
   builder.AddSmallStringProperty(
       "Value",
-      [=](wpi::SmallVectorImpl<char>& buf) -> wpi::StringRef {
+      [=](wpi::SmallVectorImpl<char>& buf) -> std::string_view {
         switch (Get()) {
           case kOn:
             return "On";
@@ -199,14 +190,15 @@
             return "Off";
         }
       },
-      [=](wpi::StringRef value) {
-        if (value == "Off")
+      [=](std::string_view value) {
+        if (value == "Off") {
           Set(kOff);
-        else if (value == "Forward")
+        } else if (value == "Forward") {
           Set(kForward);
-        else if (value == "Reverse")
+        } else if (value == "Reverse") {
           Set(kReverse);
-        else if (value == "On")
+        } else if (value == "On") {
           Set(kOn);
+        }
       });
 }
diff --git a/wpilibc/src/main/native/cpp/Resource.cpp b/wpilibc/src/main/native/cpp/Resource.cpp
index d546461..1f0e348 100644
--- a/wpilibc/src/main/native/cpp/Resource.cpp
+++ b/wpilibc/src/main/native/cpp/Resource.cpp
@@ -1,14 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Resource.h"
 
-#include "frc/ErrorBase.h"
-#include "frc/WPIErrors.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
@@ -34,19 +30,16 @@
       return i;
     }
   }
-  wpi_setWPIErrorWithContext(NoAvailableResources, resourceDesc);
-  return std::numeric_limits<uint32_t>::max();
+  throw FRC_MakeError(err::NoAvailableResources, "{}", resourceDesc);
 }
 
 uint32_t Resource::Allocate(uint32_t index, const std::string& resourceDesc) {
   std::scoped_lock lock(m_allocateMutex);
   if (index >= m_isAllocated.size()) {
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange, resourceDesc);
-    return std::numeric_limits<uint32_t>::max();
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "{}", resourceDesc);
   }
   if (m_isAllocated[index]) {
-    wpi_setWPIErrorWithContext(ResourceAlreadyAllocated, resourceDesc);
-    return std::numeric_limits<uint32_t>::max();
+    throw FRC_MakeError(err::ResourceAlreadyAllocated, "{}", resourceDesc);
   }
   m_isAllocated[index] = true;
   return index;
@@ -54,14 +47,14 @@
 
 void Resource::Free(uint32_t index) {
   std::unique_lock lock(m_allocateMutex);
-  if (index == std::numeric_limits<uint32_t>::max()) return;
-  if (index >= m_isAllocated.size()) {
-    wpi_setWPIError(NotAllocated);
+  if (index == std::numeric_limits<uint32_t>::max()) {
     return;
   }
+  if (index >= m_isAllocated.size()) {
+    throw FRC_MakeError(err::NotAllocated, "index {}", index);
+  }
   if (!m_isAllocated[index]) {
-    wpi_setWPIError(NotAllocated);
-    return;
+    throw FRC_MakeError(err::NotAllocated, "index {}", index);
   }
   m_isAllocated[index] = false;
 }
diff --git a/wpilibc/src/main/native/cpp/RobotController.cpp b/wpilibc/src/main/native/cpp/RobotController.cpp
index b33479f..8bc2d6b 100644
--- a/wpilibc/src/main/native/cpp/RobotController.cpp
+++ b/wpilibc/src/main/native/cpp/RobotController.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/RobotController.h"
 
@@ -11,152 +8,170 @@
 #include <hal/HALBase.h>
 #include <hal/Power.h>
 
-#include "frc/ErrorBase.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
 int RobotController::GetFPGAVersion() {
   int32_t status = 0;
   int version = HAL_GetFPGAVersion(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetFPGAVersion");
   return version;
 }
 
 int64_t RobotController::GetFPGARevision() {
   int32_t status = 0;
   int64_t revision = HAL_GetFPGARevision(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetFPGARevision");
   return revision;
 }
 
 uint64_t RobotController::GetFPGATime() {
   int32_t status = 0;
   uint64_t time = HAL_GetFPGATime(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetFPGATime");
   return time;
 }
 
 bool RobotController::GetUserButton() {
   int32_t status = 0;
-
   bool value = HAL_GetFPGAButton(&status);
-  wpi_setGlobalError(status);
-
+  FRC_CheckErrorStatus(status, "{}", "GetUserButton");
   return value;
 }
 
+units::volt_t RobotController::GetBatteryVoltage() {
+  int32_t status = 0;
+  double retVal = HAL_GetVinVoltage(&status);
+  FRC_CheckErrorStatus(status, "{}", "GetBatteryVoltage");
+  return units::volt_t{retVal};
+}
+
 bool RobotController::IsSysActive() {
   int32_t status = 0;
   bool retVal = HAL_GetSystemActive(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "IsSysActive");
   return retVal;
 }
 
 bool RobotController::IsBrownedOut() {
   int32_t status = 0;
   bool retVal = HAL_GetBrownedOut(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "IsBrownedOut");
   return retVal;
 }
 
 double RobotController::GetInputVoltage() {
   int32_t status = 0;
   double retVal = HAL_GetVinVoltage(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetInputVoltage");
   return retVal;
 }
 
 double RobotController::GetInputCurrent() {
   int32_t status = 0;
   double retVal = HAL_GetVinCurrent(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetInputCurrent");
   return retVal;
 }
 
 double RobotController::GetVoltage3V3() {
   int32_t status = 0;
   double retVal = HAL_GetUserVoltage3V3(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetVoltage3V3");
   return retVal;
 }
 
 double RobotController::GetCurrent3V3() {
   int32_t status = 0;
   double retVal = HAL_GetUserCurrent3V3(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetCurrent3V3");
   return retVal;
 }
 
 bool RobotController::GetEnabled3V3() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive3V3(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetEnabled3V3");
   return retVal;
 }
 
 int RobotController::GetFaultCount3V3() {
   int32_t status = 0;
   int retVal = HAL_GetUserCurrentFaults3V3(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetFaultCount3V3");
   return retVal;
 }
 
 double RobotController::GetVoltage5V() {
   int32_t status = 0;
   double retVal = HAL_GetUserVoltage5V(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetVoltage5V");
   return retVal;
 }
 
 double RobotController::GetCurrent5V() {
   int32_t status = 0;
   double retVal = HAL_GetUserCurrent5V(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetCurrent5V");
   return retVal;
 }
 
 bool RobotController::GetEnabled5V() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive5V(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetEnabled5V");
   return retVal;
 }
 
 int RobotController::GetFaultCount5V() {
   int32_t status = 0;
   int retVal = HAL_GetUserCurrentFaults5V(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetFaultCount5V");
   return retVal;
 }
 
 double RobotController::GetVoltage6V() {
   int32_t status = 0;
   double retVal = HAL_GetUserVoltage6V(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetVoltage6V");
   return retVal;
 }
 
 double RobotController::GetCurrent6V() {
   int32_t status = 0;
   double retVal = HAL_GetUserCurrent6V(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetCurrent6V");
   return retVal;
 }
 
 bool RobotController::GetEnabled6V() {
   int32_t status = 0;
   bool retVal = HAL_GetUserActive6V(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetEnabled6V");
   return retVal;
 }
 
 int RobotController::GetFaultCount6V() {
   int32_t status = 0;
   int retVal = HAL_GetUserCurrentFaults6V(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetFaultCount6V");
   return retVal;
 }
 
+units::volt_t RobotController::GetBrownoutVoltage() {
+  int32_t status = 0;
+  double retVal = HAL_GetBrownoutVoltage(&status);
+  FRC_CheckErrorStatus(status, "{}", "GetBrownoutVoltage");
+  return units::volt_t(retVal);
+}
+
+void RobotController::SetBrownoutVoltage(units::volt_t brownoutVoltage) {
+  int32_t status = 0;
+  HAL_SetBrownoutVoltage(brownoutVoltage.value(), &status);
+  FRC_CheckErrorStatus(status, "{}", "SetBrownoutVoltage");
+}
+
 CANStatus RobotController::GetCANStatus() {
   int32_t status = 0;
   float percentBusUtilization = 0;
@@ -166,10 +181,7 @@
   uint32_t transmitErrorCount = 0;
   HAL_CAN_GetCANStatus(&percentBusUtilization, &busOffCount, &txFullCount,
                        &receiveErrorCount, &transmitErrorCount, &status);
-  if (status != 0) {
-    wpi_setGlobalHALError(status);
-    return {};
-  }
+  FRC_CheckErrorStatus(status, "{}", "GetCANStatus");
   return {percentBusUtilization, static_cast<int>(busOffCount),
           static_cast<int>(txFullCount), static_cast<int>(receiveErrorCount),
           static_cast<int>(transmitErrorCount)};
diff --git a/wpilibc/src/main/native/cpp/RobotDrive.cpp b/wpilibc/src/main/native/cpp/RobotDrive.cpp
deleted file mode 100644
index 9eafe0c..0000000
--- a/wpilibc/src/main/native/cpp/RobotDrive.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/RobotDrive.h"
-
-#include <algorithm>
-#include <cmath>
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/GenericHID.h"
-#include "frc/Joystick.h"
-#include "frc/Talon.h"
-#include "frc/Utility.h"
-#include "frc/WPIErrors.h"
-
-using namespace frc;
-
-static std::shared_ptr<SpeedController> make_shared_nodelete(
-    SpeedController* ptr) {
-  return std::shared_ptr<SpeedController>(ptr, NullDeleter<SpeedController>());
-}
-
-RobotDrive::RobotDrive(int leftMotorChannel, int rightMotorChannel) {
-  InitRobotDrive();
-  m_rearLeftMotor = std::make_shared<Talon>(leftMotorChannel);
-  m_rearRightMotor = std::make_shared<Talon>(rightMotorChannel);
-  SetLeftRightMotorOutputs(0.0, 0.0);
-}
-
-RobotDrive::RobotDrive(int frontLeftMotor, int rearLeftMotor,
-                       int frontRightMotor, int rearRightMotor) {
-  InitRobotDrive();
-  m_rearLeftMotor = std::make_shared<Talon>(rearLeftMotor);
-  m_rearRightMotor = std::make_shared<Talon>(rearRightMotor);
-  m_frontLeftMotor = std::make_shared<Talon>(frontLeftMotor);
-  m_frontRightMotor = std::make_shared<Talon>(frontRightMotor);
-  SetLeftRightMotorOutputs(0.0, 0.0);
-}
-
-RobotDrive::RobotDrive(SpeedController* leftMotor,
-                       SpeedController* rightMotor) {
-  InitRobotDrive();
-  if (leftMotor == nullptr || rightMotor == nullptr) {
-    wpi_setWPIError(NullParameter);
-    m_rearLeftMotor = m_rearRightMotor = nullptr;
-    return;
-  }
-  m_rearLeftMotor = make_shared_nodelete(leftMotor);
-  m_rearRightMotor = make_shared_nodelete(rightMotor);
-}
-
-RobotDrive::RobotDrive(SpeedController& leftMotor,
-                       SpeedController& rightMotor) {
-  InitRobotDrive();
-  m_rearLeftMotor = make_shared_nodelete(&leftMotor);
-  m_rearRightMotor = make_shared_nodelete(&rightMotor);
-}
-
-RobotDrive::RobotDrive(std::shared_ptr<SpeedController> leftMotor,
-                       std::shared_ptr<SpeedController> rightMotor) {
-  InitRobotDrive();
-  if (leftMotor == nullptr || rightMotor == nullptr) {
-    wpi_setWPIError(NullParameter);
-    m_rearLeftMotor = m_rearRightMotor = nullptr;
-    return;
-  }
-  m_rearLeftMotor = leftMotor;
-  m_rearRightMotor = rightMotor;
-}
-
-RobotDrive::RobotDrive(SpeedController* frontLeftMotor,
-                       SpeedController* rearLeftMotor,
-                       SpeedController* frontRightMotor,
-                       SpeedController* rearRightMotor) {
-  InitRobotDrive();
-  if (frontLeftMotor == nullptr || rearLeftMotor == nullptr ||
-      frontRightMotor == nullptr || rearRightMotor == nullptr) {
-    wpi_setWPIError(NullParameter);
-    return;
-  }
-  m_frontLeftMotor = make_shared_nodelete(frontLeftMotor);
-  m_rearLeftMotor = make_shared_nodelete(rearLeftMotor);
-  m_frontRightMotor = make_shared_nodelete(frontRightMotor);
-  m_rearRightMotor = make_shared_nodelete(rearRightMotor);
-}
-
-RobotDrive::RobotDrive(SpeedController& frontLeftMotor,
-                       SpeedController& rearLeftMotor,
-                       SpeedController& frontRightMotor,
-                       SpeedController& rearRightMotor) {
-  InitRobotDrive();
-  m_frontLeftMotor = make_shared_nodelete(&frontLeftMotor);
-  m_rearLeftMotor = make_shared_nodelete(&rearLeftMotor);
-  m_frontRightMotor = make_shared_nodelete(&frontRightMotor);
-  m_rearRightMotor = make_shared_nodelete(&rearRightMotor);
-}
-
-RobotDrive::RobotDrive(std::shared_ptr<SpeedController> frontLeftMotor,
-                       std::shared_ptr<SpeedController> rearLeftMotor,
-                       std::shared_ptr<SpeedController> frontRightMotor,
-                       std::shared_ptr<SpeedController> rearRightMotor) {
-  InitRobotDrive();
-  if (frontLeftMotor == nullptr || rearLeftMotor == nullptr ||
-      frontRightMotor == nullptr || rearRightMotor == nullptr) {
-    wpi_setWPIError(NullParameter);
-    return;
-  }
-  m_frontLeftMotor = frontLeftMotor;
-  m_rearLeftMotor = rearLeftMotor;
-  m_frontRightMotor = frontRightMotor;
-  m_rearRightMotor = rearRightMotor;
-}
-
-void RobotDrive::Drive(double outputMagnitude, double curve) {
-  double leftOutput, rightOutput;
-  static bool reported = false;
-  if (!reported) {
-    HAL_Report(HALUsageReporting::kResourceType_RobotDrive,
-               HALUsageReporting::kRobotDrive_ArcadeRatioCurve, GetNumMotors());
-    reported = true;
-  }
-
-  if (curve < 0) {
-    double value = std::log(-curve);
-    double ratio = (value - m_sensitivity) / (value + m_sensitivity);
-    if (ratio == 0) ratio = 0.0000000001;
-    leftOutput = outputMagnitude / ratio;
-    rightOutput = outputMagnitude;
-  } else if (curve > 0) {
-    double value = std::log(curve);
-    double ratio = (value - m_sensitivity) / (value + m_sensitivity);
-    if (ratio == 0) ratio = 0.0000000001;
-    leftOutput = outputMagnitude;
-    rightOutput = outputMagnitude / ratio;
-  } else {
-    leftOutput = outputMagnitude;
-    rightOutput = outputMagnitude;
-  }
-  SetLeftRightMotorOutputs(leftOutput, rightOutput);
-}
-
-void RobotDrive::TankDrive(GenericHID* leftStick, GenericHID* rightStick,
-                           bool squaredInputs) {
-  if (leftStick == nullptr || rightStick == nullptr) {
-    wpi_setWPIError(NullParameter);
-    return;
-  }
-  TankDrive(leftStick->GetY(), rightStick->GetY(), squaredInputs);
-}
-
-void RobotDrive::TankDrive(GenericHID& leftStick, GenericHID& rightStick,
-                           bool squaredInputs) {
-  TankDrive(leftStick.GetY(), rightStick.GetY(), squaredInputs);
-}
-
-void RobotDrive::TankDrive(GenericHID* leftStick, int leftAxis,
-                           GenericHID* rightStick, int rightAxis,
-                           bool squaredInputs) {
-  if (leftStick == nullptr || rightStick == nullptr) {
-    wpi_setWPIError(NullParameter);
-    return;
-  }
-  TankDrive(leftStick->GetRawAxis(leftAxis), rightStick->GetRawAxis(rightAxis),
-            squaredInputs);
-}
-
-void RobotDrive::TankDrive(GenericHID& leftStick, int leftAxis,
-                           GenericHID& rightStick, int rightAxis,
-                           bool squaredInputs) {
-  TankDrive(leftStick.GetRawAxis(leftAxis), rightStick.GetRawAxis(rightAxis),
-            squaredInputs);
-}
-
-void RobotDrive::TankDrive(double leftValue, double rightValue,
-                           bool squaredInputs) {
-  static bool reported = false;
-  if (!reported) {
-    HAL_Report(HALUsageReporting::kResourceType_RobotDrive,
-               HALUsageReporting::kRobotDrive_Tank, GetNumMotors());
-    reported = true;
-  }
-
-  leftValue = Limit(leftValue);
-  rightValue = Limit(rightValue);
-
-  // square the inputs (while preserving the sign) to increase fine control
-  // while permitting full power
-  if (squaredInputs) {
-    leftValue = std::copysign(leftValue * leftValue, leftValue);
-    rightValue = std::copysign(rightValue * rightValue, rightValue);
-  }
-
-  SetLeftRightMotorOutputs(leftValue, rightValue);
-}
-
-void RobotDrive::ArcadeDrive(GenericHID* stick, bool squaredInputs) {
-  // simply call the full-featured ArcadeDrive with the appropriate values
-  ArcadeDrive(stick->GetY(), stick->GetX(), squaredInputs);
-}
-
-void RobotDrive::ArcadeDrive(GenericHID& stick, bool squaredInputs) {
-  // simply call the full-featured ArcadeDrive with the appropriate values
-  ArcadeDrive(stick.GetY(), stick.GetX(), squaredInputs);
-}
-
-void RobotDrive::ArcadeDrive(GenericHID* moveStick, int moveAxis,
-                             GenericHID* rotateStick, int rotateAxis,
-                             bool squaredInputs) {
-  double moveValue = moveStick->GetRawAxis(moveAxis);
-  double rotateValue = rotateStick->GetRawAxis(rotateAxis);
-
-  ArcadeDrive(moveValue, rotateValue, squaredInputs);
-}
-
-void RobotDrive::ArcadeDrive(GenericHID& moveStick, int moveAxis,
-                             GenericHID& rotateStick, int rotateAxis,
-                             bool squaredInputs) {
-  double moveValue = moveStick.GetRawAxis(moveAxis);
-  double rotateValue = rotateStick.GetRawAxis(rotateAxis);
-
-  ArcadeDrive(moveValue, rotateValue, squaredInputs);
-}
-
-void RobotDrive::ArcadeDrive(double moveValue, double rotateValue,
-                             bool squaredInputs) {
-  static bool reported = false;
-  if (!reported) {
-    HAL_Report(HALUsageReporting::kResourceType_RobotDrive,
-               HALUsageReporting::kRobotDrive_ArcadeStandard, GetNumMotors());
-    reported = true;
-  }
-
-  // local variables to hold the computed PWM values for the motors
-  double leftMotorOutput;
-  double rightMotorOutput;
-
-  moveValue = Limit(moveValue);
-  rotateValue = Limit(rotateValue);
-
-  // square the inputs (while preserving the sign) to increase fine control
-  // while permitting full power
-  if (squaredInputs) {
-    moveValue = std::copysign(moveValue * moveValue, moveValue);
-    rotateValue = std::copysign(rotateValue * rotateValue, rotateValue);
-  }
-
-  if (moveValue > 0.0) {
-    if (rotateValue > 0.0) {
-      leftMotorOutput = moveValue - rotateValue;
-      rightMotorOutput = std::max(moveValue, rotateValue);
-    } else {
-      leftMotorOutput = std::max(moveValue, -rotateValue);
-      rightMotorOutput = moveValue + rotateValue;
-    }
-  } else {
-    if (rotateValue > 0.0) {
-      leftMotorOutput = -std::max(-moveValue, rotateValue);
-      rightMotorOutput = moveValue + rotateValue;
-    } else {
-      leftMotorOutput = moveValue - rotateValue;
-      rightMotorOutput = -std::max(-moveValue, -rotateValue);
-    }
-  }
-  SetLeftRightMotorOutputs(leftMotorOutput, rightMotorOutput);
-}
-
-void RobotDrive::MecanumDrive_Cartesian(double x, double y, double rotation,
-                                        double gyroAngle) {
-  static bool reported = false;
-  if (!reported) {
-    HAL_Report(HALUsageReporting::kResourceType_RobotDrive,
-               HALUsageReporting::kRobotDrive_MecanumCartesian, GetNumMotors());
-    reported = true;
-  }
-
-  double xIn = x;
-  double yIn = y;
-  // Negate y for the joystick.
-  yIn = -yIn;
-  // Compensate for gyro angle.
-  RotateVector(xIn, yIn, gyroAngle);
-
-  double wheelSpeeds[kMaxNumberOfMotors];
-  wheelSpeeds[kFrontLeftMotor] = xIn + yIn + rotation;
-  wheelSpeeds[kFrontRightMotor] = -xIn + yIn - rotation;
-  wheelSpeeds[kRearLeftMotor] = -xIn + yIn + rotation;
-  wheelSpeeds[kRearRightMotor] = xIn + yIn - rotation;
-
-  Normalize(wheelSpeeds);
-
-  m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_maxOutput);
-  m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_maxOutput);
-  m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_maxOutput);
-  m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_maxOutput);
-
-  Feed();
-}
-
-void RobotDrive::MecanumDrive_Polar(double magnitude, double direction,
-                                    double rotation) {
-  static bool reported = false;
-  if (!reported) {
-    HAL_Report(HALUsageReporting::kResourceType_RobotDrive,
-               HALUsageReporting::kRobotDrive_MecanumPolar, GetNumMotors());
-    reported = true;
-  }
-
-  // Normalized for full power along the Cartesian axes.
-  magnitude = Limit(magnitude) * std::sqrt(2.0);
-  // The rollers are at 45 degree angles.
-  double dirInRad = (direction + 45.0) * 3.14159 / 180.0;
-  double cosD = std::cos(dirInRad);
-  double sinD = std::sin(dirInRad);
-
-  double wheelSpeeds[kMaxNumberOfMotors];
-  wheelSpeeds[kFrontLeftMotor] = sinD * magnitude + rotation;
-  wheelSpeeds[kFrontRightMotor] = cosD * magnitude - rotation;
-  wheelSpeeds[kRearLeftMotor] = cosD * magnitude + rotation;
-  wheelSpeeds[kRearRightMotor] = sinD * magnitude - rotation;
-
-  Normalize(wheelSpeeds);
-
-  m_frontLeftMotor->Set(wheelSpeeds[kFrontLeftMotor] * m_maxOutput);
-  m_frontRightMotor->Set(wheelSpeeds[kFrontRightMotor] * m_maxOutput);
-  m_rearLeftMotor->Set(wheelSpeeds[kRearLeftMotor] * m_maxOutput);
-  m_rearRightMotor->Set(wheelSpeeds[kRearRightMotor] * m_maxOutput);
-
-  Feed();
-}
-
-void RobotDrive::HolonomicDrive(double magnitude, double direction,
-                                double rotation) {
-  MecanumDrive_Polar(magnitude, direction, rotation);
-}
-
-void RobotDrive::SetLeftRightMotorOutputs(double leftOutput,
-                                          double rightOutput) {
-  wpi_assert(m_rearLeftMotor != nullptr && m_rearRightMotor != nullptr);
-
-  if (m_frontLeftMotor != nullptr)
-    m_frontLeftMotor->Set(Limit(leftOutput) * m_maxOutput);
-  m_rearLeftMotor->Set(Limit(leftOutput) * m_maxOutput);
-
-  if (m_frontRightMotor != nullptr)
-    m_frontRightMotor->Set(-Limit(rightOutput) * m_maxOutput);
-  m_rearRightMotor->Set(-Limit(rightOutput) * m_maxOutput);
-
-  Feed();
-}
-
-void RobotDrive::SetInvertedMotor(MotorType motor, bool isInverted) {
-  if (motor < 0 || motor > 3) {
-    wpi_setWPIError(InvalidMotorIndex);
-    return;
-  }
-  switch (motor) {
-    case kFrontLeftMotor:
-      m_frontLeftMotor->SetInverted(isInverted);
-      break;
-    case kFrontRightMotor:
-      m_frontRightMotor->SetInverted(isInverted);
-      break;
-    case kRearLeftMotor:
-      m_rearLeftMotor->SetInverted(isInverted);
-      break;
-    case kRearRightMotor:
-      m_rearRightMotor->SetInverted(isInverted);
-      break;
-  }
-}
-
-void RobotDrive::SetSensitivity(double sensitivity) {
-  m_sensitivity = sensitivity;
-}
-
-void RobotDrive::SetMaxOutput(double maxOutput) { m_maxOutput = maxOutput; }
-
-void RobotDrive::GetDescription(wpi::raw_ostream& desc) const {
-  desc << "RobotDrive";
-}
-
-void RobotDrive::StopMotor() {
-  if (m_frontLeftMotor != nullptr) m_frontLeftMotor->StopMotor();
-  if (m_frontRightMotor != nullptr) m_frontRightMotor->StopMotor();
-  if (m_rearLeftMotor != nullptr) m_rearLeftMotor->StopMotor();
-  if (m_rearRightMotor != nullptr) m_rearRightMotor->StopMotor();
-  Feed();
-}
-
-void RobotDrive::InitRobotDrive() { SetSafetyEnabled(true); }
-
-double RobotDrive::Limit(double number) {
-  if (number > 1.0) {
-    return 1.0;
-  }
-  if (number < -1.0) {
-    return -1.0;
-  }
-  return number;
-}
-
-void RobotDrive::Normalize(double* wheelSpeeds) {
-  double maxMagnitude = std::fabs(wheelSpeeds[0]);
-  for (int i = 1; i < kMaxNumberOfMotors; i++) {
-    double temp = std::fabs(wheelSpeeds[i]);
-    if (maxMagnitude < temp) maxMagnitude = temp;
-  }
-  if (maxMagnitude > 1.0) {
-    for (int i = 0; i < kMaxNumberOfMotors; i++) {
-      wheelSpeeds[i] = wheelSpeeds[i] / maxMagnitude;
-    }
-  }
-}
-
-void RobotDrive::RotateVector(double& x, double& y, double angle) {
-  double cosA = std::cos(angle * (3.14159 / 180.0));
-  double sinA = std::sin(angle * (3.14159 / 180.0));
-  double xOut = x * cosA - y * sinA;
-  double yOut = x * sinA + y * cosA;
-  x = xOut;
-  y = yOut;
-}
diff --git a/wpilibc/src/main/native/cpp/RobotState.cpp b/wpilibc/src/main/native/cpp/RobotState.cpp
index 530cee3..651a644 100644
--- a/wpilibc/src/main/native/cpp/RobotState.cpp
+++ b/wpilibc/src/main/native/cpp/RobotState.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/RobotState.h"
 
@@ -12,23 +9,29 @@
 using namespace frc;
 
 bool RobotState::IsDisabled() {
-  return DriverStation::GetInstance().IsDisabled();
+  return DriverStation::IsDisabled();
 }
 
 bool RobotState::IsEnabled() {
-  return DriverStation::GetInstance().IsEnabled();
+  return DriverStation::IsEnabled();
 }
 
 bool RobotState::IsEStopped() {
-  return DriverStation::GetInstance().IsEStopped();
+  return DriverStation::IsEStopped();
 }
 
 bool RobotState::IsOperatorControl() {
-  return DriverStation::GetInstance().IsOperatorControl();
+  return IsTeleop();
+}
+
+bool RobotState::IsTeleop() {
+  return DriverStation::IsTeleop();
 }
 
 bool RobotState::IsAutonomous() {
-  return DriverStation::GetInstance().IsAutonomous();
+  return DriverStation::IsAutonomous();
 }
 
-bool RobotState::IsTest() { return DriverStation::GetInstance().IsTest(); }
+bool RobotState::IsTest() {
+  return DriverStation::IsTest();
+}
diff --git a/wpilibc/src/main/native/cpp/SD540.cpp b/wpilibc/src/main/native/cpp/SD540.cpp
deleted file mode 100644
index 9611f66..0000000
--- a/wpilibc/src/main/native/cpp/SD540.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/SD540.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-SD540::SD540(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.05, 1.55, 1.50, 1.44, 0.94);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_MindsensorsSD540,
-             GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "SD540", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/SPI.cpp b/wpilibc/src/main/native/cpp/SPI.cpp
index d51fa3b..bdcee45 100644
--- a/wpilibc/src/main/native/cpp/SPI.cpp
+++ b/wpilibc/src/main/native/cpp/SPI.cpp
@@ -1,13 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/SPI.h"
 
 #include <cstring>
+#include <memory>
 #include <utility>
 
 #include <hal/FRCUsageReporting.h>
@@ -16,8 +14,8 @@
 #include <wpi/mutex.h>
 
 #include "frc/DigitalSource.h"
+#include "frc/Errors.h"
 #include "frc/Notifier.h"
-#include "frc/WPIErrors.h"
 
 using namespace frc;
 
@@ -27,7 +25,7 @@
  public:
   Accumulator(HAL_SPIPort port, int xferSize, int validMask, int validValue,
               int dataShift, int dataSize, bool isSigned, bool bigEndian)
-      : m_notifier([=]() {
+      : m_notifier([=] {
           std::scoped_lock lock(m_mutex);
           Update();
         }),
@@ -79,7 +77,7 @@
     // get amount of data available
     int32_t numToRead =
         HAL_ReadSPIAutoReceivedData(m_port, m_buf, 0, 0, &status);
-    if (status != 0) return;  // error reading
+    FRC_CheckErrorStatus(status, "Port {}", m_port);
 
     // only get whole responses; +1 is for timestamp
     numToRead -= numToRead % m_xferSize;
@@ -87,11 +85,13 @@
       numToRead = m_xferSize * kAccumulateDepth;
       done = false;
     }
-    if (numToRead == 0) return;  // no samples
+    if (numToRead == 0) {
+      return;  // no samples
+    }
 
     // read buffered data
     HAL_ReadSPIAutoReceivedData(m_port, m_buf, numToRead, 0, &status);
-    if (status != 0) return;  // error reading
+    FRC_CheckErrorStatus(status, "Port {}", m_port);
 
     // loop over all responses
     for (int32_t off = 0; off < numToRead; off += m_xferSize) {
@@ -118,7 +118,9 @@
         int32_t data = static_cast<int32_t>(resp >> m_dataShift);
         data &= m_dataMax - 1;
         // 2s complement conversion if signed MSB is set
-        if (m_isSigned && (data & m_dataMsbMask) != 0) data -= m_dataMax;
+        if (m_isSigned && (data & m_dataMsbMask) != 0) {
+          data -= m_dataMax;
+        }
         // center offset
         int32_t dataNoCenter = data;
         data -= m_center;
@@ -127,18 +129,19 @@
           m_value += data;
           if (m_count != 0) {
             // timestamps use the 1us FPGA clock; also handle rollover
-            if (timestamp >= m_lastTimestamp)
+            if (timestamp >= m_lastTimestamp) {
               m_integratedValue +=
                   dataNoCenter *
                       static_cast<int32_t>(timestamp - m_lastTimestamp) * 1e-6 -
                   m_integratedCenter;
-            else
+            } else {
               m_integratedValue +=
                   dataNoCenter *
                       static_cast<int32_t>((1ULL << 32) - m_lastTimestamp +
                                            timestamp) *
                       1e-6 -
                   m_integratedCenter;
+            }
           }
         }
         ++m_count;
@@ -155,15 +158,23 @@
 SPI::SPI(Port port) : m_port(static_cast<HAL_SPIPort>(port)) {
   int32_t status = 0;
   HAL_InitializeSPI(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 
   HAL_Report(HALUsageReporting::kResourceType_SPI,
              static_cast<uint8_t>(port) + 1);
 }
 
-SPI::~SPI() { HAL_CloseSPI(m_port); }
+SPI::~SPI() {
+  HAL_CloseSPI(m_port);
+}
 
-void SPI::SetClockRate(int hz) { HAL_SetSPISpeed(m_port, hz); }
+SPI::Port SPI::GetPort() const {
+  return static_cast<Port>(static_cast<int>(m_port));
+}
+
+void SPI::SetClockRate(int hz) {
+  HAL_SetSPISpeed(m_port, hz);
+}
 
 void SPI::SetMSBFirst() {
   m_msbFirst = true;
@@ -208,13 +219,13 @@
 void SPI::SetChipSelectActiveHigh() {
   int32_t status = 0;
   HAL_SetSPIChipSelectActiveHigh(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void SPI::SetChipSelectActiveLow() {
   int32_t status = 0;
   HAL_SetSPIChipSelectActiveLow(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 int SPI::Write(uint8_t* data, int size) {
@@ -244,26 +255,27 @@
 void SPI::InitAuto(int bufferSize) {
   int32_t status = 0;
   HAL_InitSPIAuto(m_port, bufferSize, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void SPI::FreeAuto() {
   int32_t status = 0;
   HAL_FreeSPIAuto(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
-void SPI::SetAutoTransmitData(wpi::ArrayRef<uint8_t> dataToSend, int zeroSize) {
+void SPI::SetAutoTransmitData(wpi::span<const uint8_t> dataToSend,
+                              int zeroSize) {
   int32_t status = 0;
   HAL_SetSPIAutoTransmitData(m_port, dataToSend.data(), dataToSend.size(),
                              zeroSize, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void SPI::StartAutoRate(units::second_t period) {
   int32_t status = 0;
-  HAL_StartSPIAutoRate(m_port, period.to<double>(), &status);
-  wpi_setHALError(status);
+  HAL_StartSPIAutoRate(m_port, period.value(), &status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void SPI::StartAutoRate(double period) {
@@ -272,42 +284,38 @@
 
 void SPI::StartAutoTrigger(DigitalSource& source, bool rising, bool falling) {
   int32_t status = 0;
-  HAL_StartSPIAutoTrigger(
-      m_port, source.GetPortHandleForRouting(),
-      (HAL_AnalogTriggerType)source.GetAnalogTriggerTypeForRouting(), rising,
-      falling, &status);
-  wpi_setHALError(status);
+  HAL_StartSPIAutoTrigger(m_port, source.GetPortHandleForRouting(),
+                          static_cast<HAL_AnalogTriggerType>(
+                              source.GetAnalogTriggerTypeForRouting()),
+                          rising, falling, &status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void SPI::StopAuto() {
   int32_t status = 0;
   HAL_StopSPIAuto(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void SPI::ForceAutoRead() {
   int32_t status = 0;
   HAL_ForceSPIAutoRead(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 int SPI::ReadAutoReceivedData(uint32_t* buffer, int numToRead,
                               units::second_t timeout) {
   int32_t status = 0;
   int32_t val = HAL_ReadSPIAutoReceivedData(m_port, buffer, numToRead,
-                                            timeout.to<double>(), &status);
-  wpi_setHALError(status);
+                                            timeout.value(), &status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
   return val;
 }
 
-int SPI::ReadAutoReceivedData(uint32_t* buffer, int numToRead, double timeout) {
-  return ReadAutoReceivedData(buffer, numToRead, units::second_t(timeout));
-}
-
 int SPI::GetAutoDroppedCount() {
   int32_t status = 0;
   int32_t val = HAL_GetSPIAutoDroppedCount(m_port, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
   return val;
 }
 
@@ -316,7 +324,7 @@
   int32_t status = 0;
   HAL_ConfigureSPIAutoStall(m_port, csToSclkTicks, stallTicks, pow2BytesPerRead,
                             &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "Port {}", m_port);
 }
 
 void SPI::InitAccumulator(units::second_t period, int cmd, int xferSize,
@@ -341,8 +349,9 @@
   SetAutoTransmitData(cmdBytes, xferSize - 4);
   StartAutoRate(period);
 
-  m_accum.reset(new Accumulator(m_port, xferSize, validMask, validValue,
-                                dataShift, dataSize, isSigned, bigEndian));
+  m_accum =
+      std::make_unique<Accumulator>(m_port, xferSize, validMask, validValue,
+                                    dataShift, dataSize, isSigned, bigEndian);
   m_accum->m_notifier.StartPeriodic(period * kAccumulateDepth / 2);
 }
 
@@ -359,7 +368,9 @@
 }
 
 void SPI::ResetAccumulator() {
-  if (!m_accum) return;
+  if (!m_accum) {
+    return;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->m_value = 0;
   m_accum->m_count = 0;
@@ -369,43 +380,57 @@
 }
 
 void SPI::SetAccumulatorCenter(int center) {
-  if (!m_accum) return;
+  if (!m_accum) {
+    return;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->m_center = center;
 }
 
 void SPI::SetAccumulatorDeadband(int deadband) {
-  if (!m_accum) return;
+  if (!m_accum) {
+    return;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->m_deadband = deadband;
 }
 
 int SPI::GetAccumulatorLastValue() const {
-  if (!m_accum) return 0;
+  if (!m_accum) {
+    return 0;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->Update();
   return m_accum->m_lastValue;
 }
 
 int64_t SPI::GetAccumulatorValue() const {
-  if (!m_accum) return 0;
+  if (!m_accum) {
+    return 0;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->Update();
   return m_accum->m_value;
 }
 
 int64_t SPI::GetAccumulatorCount() const {
-  if (!m_accum) return 0;
+  if (!m_accum) {
+    return 0;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->Update();
   return m_accum->m_count;
 }
 
 double SPI::GetAccumulatorAverage() const {
-  if (!m_accum) return 0;
+  if (!m_accum) {
+    return 0;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->Update();
-  if (m_accum->m_count == 0) return 0.0;
+  if (m_accum->m_count == 0) {
+    return 0.0;
+  }
   return static_cast<double>(m_accum->m_value) / m_accum->m_count;
 }
 
@@ -422,23 +447,31 @@
 }
 
 void SPI::SetAccumulatorIntegratedCenter(double center) {
-  if (!m_accum) return;
+  if (!m_accum) {
+    return;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->m_integratedCenter = center;
 }
 
 double SPI::GetAccumulatorIntegratedValue() const {
-  if (!m_accum) return 0;
+  if (!m_accum) {
+    return 0;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->Update();
   return m_accum->m_integratedValue;
 }
 
 double SPI::GetAccumulatorIntegratedAverage() const {
-  if (!m_accum) return 0;
+  if (!m_accum) {
+    return 0;
+  }
   std::scoped_lock lock(m_accum->m_mutex);
   m_accum->Update();
-  if (m_accum->m_count <= 1) return 0.0;
+  if (m_accum->m_count <= 1) {
+    return 0.0;
+  }
   // count-1 due to not integrating the first value received
   return m_accum->m_integratedValue / (m_accum->m_count - 1);
 }
diff --git a/wpilibc/src/main/native/cpp/ScopedTracer.cpp b/wpilibc/src/main/native/cpp/ScopedTracer.cpp
index 2024a65..3c8fc80 100644
--- a/wpilibc/src/main/native/cpp/ScopedTracer.cpp
+++ b/wpilibc/src/main/native/cpp/ScopedTracer.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/ScopedTracer.h"
 
@@ -11,8 +8,8 @@
 
 using namespace frc;
 
-ScopedTracer::ScopedTracer(wpi::Twine name, wpi::raw_ostream& os)
-    : m_name(name.str()), m_os(os) {
+ScopedTracer::ScopedTracer(std::string_view name, wpi::raw_ostream& os)
+    : m_name(name), m_os(os) {
   m_tracer.ResetTimer();
 }
 
diff --git a/wpilibc/src/main/native/cpp/SensorUtil.cpp b/wpilibc/src/main/native/cpp/SensorUtil.cpp
index f86c72c..55aacce 100644
--- a/wpilibc/src/main/native/cpp/SensorUtil.cpp
+++ b/wpilibc/src/main/native/cpp/SensorUtil.cpp
@@ -1,36 +1,30 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/SensorUtil.h"
 
 #include <hal/AnalogInput.h>
 #include <hal/AnalogOutput.h>
 #include <hal/DIO.h>
-#include <hal/PDP.h>
 #include <hal/PWM.h>
 #include <hal/Ports.h>
 #include <hal/Relay.h>
-#include <hal/Solenoid.h>
 
 using namespace frc;
 
 const int SensorUtil::kDigitalChannels = HAL_GetNumDigitalChannels();
 const int SensorUtil::kAnalogInputs = HAL_GetNumAnalogInputs();
 const int SensorUtil::kAnalogOutputs = HAL_GetNumAnalogOutputs();
-const int SensorUtil::kSolenoidChannels = HAL_GetNumSolenoidChannels();
-const int SensorUtil::kSolenoidModules = HAL_GetNumPCMModules();
 const int SensorUtil::kPwmChannels = HAL_GetNumPWMChannels();
 const int SensorUtil::kRelayChannels = HAL_GetNumRelayHeaders();
-const int SensorUtil::kPDPChannels = HAL_GetNumPDPChannels();
 
-int SensorUtil::GetDefaultSolenoidModule() { return 0; }
+int SensorUtil::GetDefaultCTREPCMModule() {
+  return 0;
+}
 
-bool SensorUtil::CheckSolenoidModule(int moduleNumber) {
-  return HAL_CheckSolenoidModule(moduleNumber);
+int SensorUtil::GetDefaultREVPHModule() {
+  return 1;
 }
 
 bool SensorUtil::CheckDigitalChannel(int channel) {
@@ -52,15 +46,3 @@
 bool SensorUtil::CheckAnalogOutputChannel(int channel) {
   return HAL_CheckAnalogOutputChannel(channel);
 }
-
-bool SensorUtil::CheckSolenoidChannel(int channel) {
-  return HAL_CheckSolenoidChannel(channel);
-}
-
-bool SensorUtil::CheckPDPChannel(int channel) {
-  return HAL_CheckPDPChannel(channel);
-}
-
-bool SensorUtil::CheckPDPModule(int module) {
-  return HAL_CheckPDPModule(module);
-}
diff --git a/wpilibc/src/main/native/cpp/SerialPort.cpp b/wpilibc/src/main/native/cpp/SerialPort.cpp
index e092fc2..fb984f1 100644
--- a/wpilibc/src/main/native/cpp/SerialPort.cpp
+++ b/wpilibc/src/main/native/cpp/SerialPort.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/SerialPort.h"
 
@@ -12,6 +9,8 @@
 #include <hal/FRCUsageReporting.h>
 #include <hal/SerialPort.h>
 
+#include "frc/Errors.h"
+
 using namespace frc;
 
 SerialPort::SerialPort(int baudRate, Port port, int dataBits,
@@ -21,20 +20,18 @@
 
   m_portHandle =
       HAL_InitializeSerialPort(static_cast<HAL_SerialPort>(port), &status);
-  wpi_setHALError(status);
-  // Don't continue if initialization failed
-  if (status < 0) return;
+  FRC_CheckErrorStatus(status, "Port {}", port);
   HAL_SetSerialBaudRate(m_portHandle, baudRate, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSerialBaudRate {}", baudRate);
   HAL_SetSerialDataBits(m_portHandle, dataBits, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSerialDataBits {}", dataBits);
   HAL_SetSerialParity(m_portHandle, parity, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSerialParity {}", parity);
   HAL_SetSerialStopBits(m_portHandle, stopBits, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSerialStopBits {}", stopBits);
 
   // Set the default timeout to 5 seconds.
-  SetTimeout(5.0);
+  SetTimeout(5_s);
 
   // Don't wait until the buffer is full to transmit.
   SetWriteBufferMode(kFlushOnAccess);
@@ -45,30 +42,26 @@
              static_cast<uint8_t>(port) + 1);
 }
 
-SerialPort::SerialPort(int baudRate, const wpi::Twine& portName, Port port,
+SerialPort::SerialPort(int baudRate, std::string_view portName, Port port,
                        int dataBits, SerialPort::Parity parity,
                        SerialPort::StopBits stopBits) {
   int32_t status = 0;
 
-  wpi::SmallVector<char, 64> buf;
-  const char* portNameC = portName.toNullTerminatedStringRef(buf).data();
-
-  m_portHandle = HAL_InitializeSerialPortDirect(
-      static_cast<HAL_SerialPort>(port), portNameC, &status);
-  wpi_setHALError(status);
-  // Don't continue if initialization failed
-  if (status < 0) return;
+  m_portHandle =
+      HAL_InitializeSerialPortDirect(static_cast<HAL_SerialPort>(port),
+                                     std::string(portName).c_str(), &status);
+  FRC_CheckErrorStatus(status, "Port {}", port);
   HAL_SetSerialBaudRate(m_portHandle, baudRate, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSerialBaudRate {}", baudRate);
   HAL_SetSerialDataBits(m_portHandle, dataBits, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSerialDataBits {}", dataBits);
   HAL_SetSerialParity(m_portHandle, parity, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSerialParity {}", parity);
   HAL_SetSerialStopBits(m_portHandle, stopBits, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetSerialStopBits {}", stopBits);
 
   // Set the default timeout to 5 seconds.
-  SetTimeout(5.0);
+  SetTimeout(5_s);
 
   // Don't wait until the buffer is full to transmit.
   SetWriteBufferMode(kFlushOnAccess);
@@ -82,85 +75,85 @@
 SerialPort::~SerialPort() {
   int32_t status = 0;
   HAL_CloseSerial(m_portHandle, &status);
-  wpi_setHALError(status);
+  FRC_ReportError(status, "{}", "CloseSerial");
 }
 
 void SerialPort::SetFlowControl(SerialPort::FlowControl flowControl) {
   int32_t status = 0;
   HAL_SetSerialFlowControl(m_portHandle, flowControl, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetFlowControl {}", flowControl);
 }
 
 void SerialPort::EnableTermination(char terminator) {
   int32_t status = 0;
   HAL_EnableSerialTermination(m_portHandle, terminator, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "EnableTermination {}", terminator);
 }
 
 void SerialPort::DisableTermination() {
   int32_t status = 0;
   HAL_DisableSerialTermination(m_portHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "DisableTermination");
 }
 
 int SerialPort::GetBytesReceived() {
   int32_t status = 0;
   int retVal = HAL_GetSerialBytesReceived(m_portHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetBytesReceived");
   return retVal;
 }
 
 int SerialPort::Read(char* buffer, int count) {
   int32_t status = 0;
   int retVal = HAL_ReadSerial(m_portHandle, buffer, count, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Read");
   return retVal;
 }
 
 int SerialPort::Write(const char* buffer, int count) {
-  return Write(wpi::StringRef(buffer, static_cast<size_t>(count)));
+  return Write(std::string_view(buffer, static_cast<size_t>(count)));
 }
 
-int SerialPort::Write(wpi::StringRef buffer) {
+int SerialPort::Write(std::string_view buffer) {
   int32_t status = 0;
   int retVal =
       HAL_WriteSerial(m_portHandle, buffer.data(), buffer.size(), &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Write");
   return retVal;
 }
 
-void SerialPort::SetTimeout(double timeout) {
+void SerialPort::SetTimeout(units::second_t timeout) {
   int32_t status = 0;
-  HAL_SetSerialTimeout(m_portHandle, timeout, &status);
-  wpi_setHALError(status);
+  HAL_SetSerialTimeout(m_portHandle, timeout.value(), &status);
+  FRC_CheckErrorStatus(status, "{}", "SetTimeout");
 }
 
 void SerialPort::SetReadBufferSize(int size) {
   int32_t status = 0;
   HAL_SetSerialReadBufferSize(m_portHandle, size, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetReadBufferSize {}", size);
 }
 
 void SerialPort::SetWriteBufferSize(int size) {
   int32_t status = 0;
   HAL_SetSerialWriteBufferSize(m_portHandle, size, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetWriteBufferSize {}", size);
 }
 
 void SerialPort::SetWriteBufferMode(SerialPort::WriteBufferMode mode) {
   int32_t status = 0;
   HAL_SetSerialWriteMode(m_portHandle, mode, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "SetWriteBufferMode {}", mode);
 }
 
 void SerialPort::Flush() {
   int32_t status = 0;
   HAL_FlushSerial(m_portHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Flush");
 }
 
 void SerialPort::Reset() {
   int32_t status = 0;
   HAL_ClearSerial(m_portHandle, &status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "Reset");
 }
diff --git a/wpilibc/src/main/native/cpp/Servo.cpp b/wpilibc/src/main/native/cpp/Servo.cpp
index 5edcebc..c410bff 100644
--- a/wpilibc/src/main/native/cpp/Servo.cpp
+++ b/wpilibc/src/main/native/cpp/Servo.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Servo.h"
 
 #include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 using namespace frc;
 
@@ -28,14 +24,20 @@
   SetPeriodMultiplier(kPeriodMultiplier_4X);
 
   HAL_Report(HALUsageReporting::kResourceType_Servo, channel + 1);
-  SendableRegistry::GetInstance().SetName(this, "Servo", channel);
+  wpi::SendableRegistry::SetName(this, "Servo", channel);
 }
 
-void Servo::Set(double value) { SetPosition(value); }
+void Servo::Set(double value) {
+  SetPosition(value);
+}
 
-void Servo::SetOffline() { SetRaw(0); }
+void Servo::SetOffline() {
+  SetRaw(0);
+}
 
-double Servo::Get() const { return GetPosition(); }
+double Servo::Get() const {
+  return GetPosition();
+}
 
 void Servo::SetAngle(double degrees) {
   if (degrees < kMinServoAngle) {
@@ -51,14 +53,18 @@
   return GetPosition() * GetServoAngleRange() + kMinServoAngle;
 }
 
-double Servo::GetMaxAngle() const { return kMaxServoAngle; }
+double Servo::GetMaxAngle() const {
+  return kMaxServoAngle;
+}
 
-double Servo::GetMinAngle() const { return kMinServoAngle; }
+double Servo::GetMinAngle() const {
+  return kMinServoAngle;
+}
 
-void Servo::InitSendable(SendableBuilder& builder) {
+void Servo::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Servo");
   builder.AddDoubleProperty(
-      "Value", [=]() { return Get(); }, [=](double value) { Set(value); });
+      "Value", [=] { return Get(); }, [=](double value) { Set(value); });
 }
 
 double Servo::GetServoAngleRange() const {
diff --git a/wpilibc/src/main/native/cpp/Solenoid.cpp b/wpilibc/src/main/native/cpp/Solenoid.cpp
index b5abf20..49000c2 100644
--- a/wpilibc/src/main/native/cpp/Solenoid.cpp
+++ b/wpilibc/src/main/native/cpp/Solenoid.cpp
@@ -1,103 +1,81 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Solenoid.h"
 
 #include <utility>
 
 #include <hal/FRCUsageReporting.h>
-#include <hal/HALBase.h>
-#include <hal/Ports.h>
-#include <hal/Solenoid.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/SensorUtil.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
-Solenoid::Solenoid(int channel)
-    : Solenoid(SensorUtil::GetDefaultSolenoidModule(), channel) {}
-
-Solenoid::Solenoid(int moduleNumber, int channel)
-    : SolenoidBase(moduleNumber), m_channel(channel) {
-  if (!SensorUtil::CheckSolenoidModule(m_moduleNumber)) {
-    wpi_setWPIErrorWithContext(ModuleIndexOutOfRange,
-                               "Solenoid Module " + wpi::Twine(m_moduleNumber));
-    return;
+Solenoid::Solenoid(int module, PneumaticsModuleType moduleType, int channel)
+    : m_module{PneumaticsBase::GetForType(module, moduleType)},
+      m_channel{channel} {
+  if (!m_module->CheckSolenoidChannel(m_channel)) {
+    throw FRC_MakeError(err::ChannelIndexOutOfRange, "Channel {}", m_channel);
   }
-  if (!SensorUtil::CheckSolenoidChannel(m_channel)) {
-    wpi_setWPIErrorWithContext(ChannelIndexOutOfRange,
-                               "Solenoid Channel " + wpi::Twine(m_channel));
-    return;
-  }
+  m_mask = 1 << channel;
 
-  int32_t status = 0;
-  m_solenoidHandle = HAL_InitializeSolenoidPort(
-      HAL_GetPortWithModule(moduleNumber, channel), &status);
-  if (status != 0) {
-    wpi_setHALErrorWithRange(status, 0, HAL_GetNumSolenoidChannels(), channel);
-    m_solenoidHandle = HAL_kInvalidHandle;
-    return;
+  if (m_module->CheckAndReserveSolenoids(m_mask) != 0) {
+    throw FRC_MakeError(err::ResourceAlreadyAllocated, "Channel {}", m_channel);
   }
 
   HAL_Report(HALUsageReporting::kResourceType_Solenoid, m_channel + 1,
-             m_moduleNumber + 1);
-  SendableRegistry::GetInstance().AddLW(this, "Solenoid", m_moduleNumber,
-                                        m_channel);
+             m_module->GetModuleNumber() + 1);
+  wpi::SendableRegistry::AddLW(this, "Solenoid", m_module->GetModuleNumber(),
+                               m_channel);
 }
 
-Solenoid::~Solenoid() { HAL_FreeSolenoidPort(m_solenoidHandle); }
+Solenoid::Solenoid(PneumaticsModuleType moduleType, int channel)
+    : Solenoid{PneumaticsBase::GetDefaultForType(moduleType), moduleType,
+               channel} {}
+
+Solenoid::~Solenoid() {
+  m_module->UnreserveSolenoids(m_mask);
+}
 
 void Solenoid::Set(bool on) {
-  if (StatusIsFatal()) return;
-
-  int32_t status = 0;
-  HAL_SetSolenoid(m_solenoidHandle, on, &status);
-  wpi_setHALError(status);
+  int value = on ? (0xFFFF & m_mask) : 0;
+  m_module->SetSolenoids(m_mask, value);
 }
 
 bool Solenoid::Get() const {
-  if (StatusIsFatal()) return false;
-
-  int32_t status = 0;
-  bool value = HAL_GetSolenoid(m_solenoidHandle, &status);
-  wpi_setHALError(status);
-
-  return value;
+  int currentAll = m_module->GetSolenoids();
+  return (currentAll & m_mask) != 0;
 }
 
-void Solenoid::Toggle() { Set(!Get()); }
-
-bool Solenoid::IsBlackListed() const {
-  int value = GetPCMSolenoidBlackList(m_moduleNumber) & (1 << m_channel);
-  return (value != 0);
+void Solenoid::Toggle() {
+  Set(!Get());
 }
 
-void Solenoid::SetPulseDuration(double durationSeconds) {
-  int32_t durationMS = durationSeconds * 1000;
-  if (StatusIsFatal()) return;
-  int32_t status = 0;
-  HAL_SetOneShotDuration(m_solenoidHandle, durationMS, &status);
-  wpi_setHALError(status);
+int Solenoid::GetChannel() const {
+  return m_channel;
+}
+
+bool Solenoid::IsDisabled() const {
+  return (m_module->GetSolenoidDisabledList() & m_mask) != 0;
+}
+
+void Solenoid::SetPulseDuration(units::second_t duration) {
+  m_module->SetOneShotDuration(m_channel, duration);
 }
 
 void Solenoid::StartPulse() {
-  if (StatusIsFatal()) return;
-  int32_t status = 0;
-  HAL_FireOneShot(m_solenoidHandle, &status);
-  wpi_setHALError(status);
+  m_module->FireOneShot(m_channel);
 }
 
-void Solenoid::InitSendable(SendableBuilder& builder) {
+void Solenoid::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Solenoid");
   builder.SetActuator(true);
-  builder.SetSafeState([=]() { Set(false); });
+  builder.SetSafeState([=] { Set(false); });
   builder.AddBooleanProperty(
-      "Value", [=]() { return Get(); }, [=](bool value) { Set(value); });
+      "Value", [=] { return Get(); }, [=](bool value) { Set(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/SolenoidBase.cpp b/wpilibc/src/main/native/cpp/SolenoidBase.cpp
deleted file mode 100644
index f5f8aad..0000000
--- a/wpilibc/src/main/native/cpp/SolenoidBase.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/SolenoidBase.h"
-
-#include <hal/FRCUsageReporting.h>
-#include <hal/Solenoid.h>
-
-using namespace frc;
-
-int SolenoidBase::GetAll(int module) {
-  int value = 0;
-  int32_t status = 0;
-  value = HAL_GetAllSolenoids(module, &status);
-  wpi_setGlobalHALError(status);
-  return value;
-}
-
-int SolenoidBase::GetAll() const {
-  return SolenoidBase::GetAll(m_moduleNumber);
-}
-
-int SolenoidBase::GetPCMSolenoidBlackList(int module) {
-  int32_t status = 0;
-  return HAL_GetPCMSolenoidBlackList(module, &status);
-}
-
-int SolenoidBase::GetPCMSolenoidBlackList() const {
-  return SolenoidBase::GetPCMSolenoidBlackList(m_moduleNumber);
-}
-
-bool SolenoidBase::GetPCMSolenoidVoltageStickyFault(int module) {
-  int32_t status = 0;
-  return HAL_GetPCMSolenoidVoltageStickyFault(module, &status);
-}
-
-bool SolenoidBase::GetPCMSolenoidVoltageStickyFault() const {
-  return SolenoidBase::GetPCMSolenoidVoltageStickyFault(m_moduleNumber);
-}
-
-bool SolenoidBase::GetPCMSolenoidVoltageFault(int module) {
-  int32_t status = 0;
-  return HAL_GetPCMSolenoidVoltageFault(module, &status);
-}
-
-bool SolenoidBase::GetPCMSolenoidVoltageFault() const {
-  return SolenoidBase::GetPCMSolenoidVoltageFault(m_moduleNumber);
-}
-
-void SolenoidBase::ClearAllPCMStickyFaults(int module) {
-  int32_t status = 0;
-  return HAL_ClearAllPCMStickyFaults(module, &status);
-}
-
-void SolenoidBase::ClearAllPCMStickyFaults() {
-  SolenoidBase::ClearAllPCMStickyFaults(m_moduleNumber);
-}
-
-SolenoidBase::SolenoidBase(int moduleNumber) : m_moduleNumber(moduleNumber) {}
diff --git a/wpilibc/src/main/native/cpp/Spark.cpp b/wpilibc/src/main/native/cpp/Spark.cpp
deleted file mode 100644
index 3717df4..0000000
--- a/wpilibc/src/main/native/cpp/Spark.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/Spark.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-Spark::Spark(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.003, 1.55, 1.50, 1.46, 0.999);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_RevSPARK, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "Spark", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/SpeedController.cpp b/wpilibc/src/main/native/cpp/SpeedController.cpp
index cb3cc5b..e0b0cb2 100644
--- a/wpilibc/src/main/native/cpp/SpeedController.cpp
+++ b/wpilibc/src/main/native/cpp/SpeedController.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/SpeedController.h"
 
diff --git a/wpilibc/src/main/native/cpp/SpeedControllerGroup.cpp b/wpilibc/src/main/native/cpp/SpeedControllerGroup.cpp
index 8fea1b2..c3704df 100644
--- a/wpilibc/src/main/native/cpp/SpeedControllerGroup.cpp
+++ b/wpilibc/src/main/native/cpp/SpeedControllerGroup.cpp
@@ -1,14 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/SpeedControllerGroup.h"
 
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 using namespace frc;
 
@@ -22,11 +19,12 @@
 }
 
 void SpeedControllerGroup::Initialize() {
-  for (auto& speedController : m_speedControllers)
-    SendableRegistry::GetInstance().AddChild(this, &speedController.get());
+  for (auto& speedController : m_speedControllers) {
+    wpi::SendableRegistry::AddChild(this, &speedController.get());
+  }
   static int instances = 0;
   ++instances;
-  SendableRegistry::GetInstance().Add(this, "SpeedControllerGroup", instances);
+  wpi::SendableRegistry::Add(this, "SpeedControllerGroup", instances);
 }
 
 void SpeedControllerGroup::Set(double speed) {
@@ -46,7 +44,9 @@
   m_isInverted = isInverted;
 }
 
-bool SpeedControllerGroup::GetInverted() const { return m_isInverted; }
+bool SpeedControllerGroup::GetInverted() const {
+  return m_isInverted;
+}
 
 void SpeedControllerGroup::Disable() {
   for (auto speedController : m_speedControllers) {
@@ -60,12 +60,10 @@
   }
 }
 
-void SpeedControllerGroup::PIDWrite(double output) { Set(output); }
-
-void SpeedControllerGroup::InitSendable(SendableBuilder& builder) {
+void SpeedControllerGroup::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Speed Controller");
   builder.SetActuator(true);
-  builder.SetSafeState([=]() { StopMotor(); });
+  builder.SetSafeState([=] { StopMotor(); });
   builder.AddDoubleProperty(
-      "Value", [=]() { return Get(); }, [=](double value) { Set(value); });
+      "Value", [=] { return Get(); }, [=](double value) { Set(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/SynchronousInterrupt.cpp b/wpilibc/src/main/native/cpp/SynchronousInterrupt.cpp
new file mode 100644
index 0000000..49acd84
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/SynchronousInterrupt.cpp
@@ -0,0 +1,105 @@
+// 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.
+
+#include "frc/SynchronousInterrupt.h"
+
+#include <type_traits>
+
+#include <hal/Interrupts.h>
+#include <wpi/NullDeleter.h>
+
+#include "frc/DigitalSource.h"
+#include "frc/Errors.h"
+
+using namespace frc;
+
+SynchronousInterrupt::SynchronousInterrupt(DigitalSource& source)
+    : m_source{&source, wpi::NullDeleter<DigitalSource>()} {
+  InitSynchronousInterrupt();
+}
+SynchronousInterrupt::SynchronousInterrupt(DigitalSource* source)
+    : m_source{source, wpi::NullDeleter<DigitalSource>()} {
+  if (m_source == nullptr) {
+    FRC_CheckErrorStatus(frc::err::NullParameter, "{}", "Source is null");
+  } else {
+    InitSynchronousInterrupt();
+  }
+}
+SynchronousInterrupt::SynchronousInterrupt(
+    std::shared_ptr<DigitalSource> source)
+    : m_source{std::move(source)} {
+  if (m_source == nullptr) {
+    FRC_CheckErrorStatus(frc::err::NullParameter, "{}", "Source is null");
+  } else {
+    InitSynchronousInterrupt();
+  }
+}
+
+void SynchronousInterrupt::InitSynchronousInterrupt() {
+  int32_t status = 0;
+  m_handle = HAL_InitializeInterrupts(&status);
+  FRC_CheckErrorStatus(status, "{}", "Interrupt failed to initialize");
+  HAL_RequestInterrupts(m_handle, m_source->GetPortHandleForRouting(),
+                        static_cast<HAL_AnalogTriggerType>(
+                            m_source->GetAnalogTriggerTypeForRouting()),
+                        &status);
+  FRC_CheckErrorStatus(status, "{}", "Interrupt request failed");
+  HAL_SetInterruptUpSourceEdge(m_handle, true, false, &status);
+  FRC_CheckErrorStatus(status, "{}", "Interrupt setting up source edge failed");
+}
+
+SynchronousInterrupt::~SynchronousInterrupt() {
+  HAL_CleanInterrupts(m_handle);
+}
+
+inline SynchronousInterrupt::WaitResult operator|(
+    SynchronousInterrupt::WaitResult lhs,
+    SynchronousInterrupt::WaitResult rhs) {
+  using T = std::underlying_type_t<SynchronousInterrupt::WaitResult>;
+  return static_cast<SynchronousInterrupt::WaitResult>(static_cast<T>(lhs) |
+                                                       static_cast<T>(rhs));
+}
+
+SynchronousInterrupt::WaitResult SynchronousInterrupt::WaitForInterrupt(
+    units::second_t timeout, bool ignorePrevious) {
+  int32_t status = 0;
+  auto result =
+      HAL_WaitForInterrupt(m_handle, timeout.value(), ignorePrevious, &status);
+
+  auto rising =
+      ((result & 0xFF) != 0) ? WaitResult::kRisingEdge : WaitResult::kTimeout;
+  auto falling = ((result & 0xFF00) != 0) ? WaitResult::kFallingEdge
+                                          : WaitResult::kTimeout;
+
+  return rising | falling;
+}
+
+void SynchronousInterrupt::SetInterruptEdges(bool risingEdge,
+                                             bool fallingEdge) {
+  int32_t status = 0;
+  HAL_SetInterruptUpSourceEdge(m_handle, risingEdge, fallingEdge, &status);
+  FRC_CheckErrorStatus(status, "{}", "Interrupt setting edges failed");
+}
+
+void SynchronousInterrupt::WakeupWaitingInterrupt() {
+  int32_t status = 0;
+  HAL_ReleaseWaitingInterrupt(m_handle, &status);
+  FRC_CheckErrorStatus(status, "{}", "Interrupt wakeup failed");
+}
+
+units::second_t SynchronousInterrupt::GetRisingTimestamp() {
+  int32_t status = 0;
+  auto ts = HAL_ReadInterruptRisingTimestamp(m_handle, &status);
+  FRC_CheckErrorStatus(status, "{}", "Interrupt rising timestamp failed");
+  units::microsecond_t ms{static_cast<double>(ts)};
+  return ms;
+}
+
+units::second_t SynchronousInterrupt::GetFallingTimestamp() {
+  int32_t status = 0;
+  auto ts = HAL_ReadInterruptFallingTimestamp(m_handle, &status);
+  FRC_CheckErrorStatus(status, "{}", "Interrupt falling timestamp failed");
+  units::microsecond_t ms{static_cast<double>(ts)};
+  return ms;
+}
diff --git a/wpilibc/src/main/native/cpp/Talon.cpp b/wpilibc/src/main/native/cpp/Talon.cpp
deleted file mode 100644
index 4807d71..0000000
--- a/wpilibc/src/main/native/cpp/Talon.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/Talon.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-Talon::Talon(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.037, 1.539, 1.513, 1.487, 0.989);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_Talon, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "Talon", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/Threads.cpp b/wpilibc/src/main/native/cpp/Threads.cpp
index 7b1e647..2b9c151 100644
--- a/wpilibc/src/main/native/cpp/Threads.cpp
+++ b/wpilibc/src/main/native/cpp/Threads.cpp
@@ -1,16 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Threads.h"
 
 #include <hal/FRCUsageReporting.h>
 #include <hal/Threads.h>
 
-#include "frc/ErrorBase.h"
+#include "frc/Errors.h"
 
 namespace frc {
 
@@ -19,7 +16,7 @@
   HAL_Bool rt = false;
   auto native = thread.native_handle();
   auto ret = HAL_GetThreadPriority(&native, &rt, &status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetThreadPriority");
   *isRealTime = rt;
   return ret;
 }
@@ -28,7 +25,7 @@
   int32_t status = 0;
   HAL_Bool rt = false;
   auto ret = HAL_GetCurrentThreadPriority(&rt, &status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "GetCurrentThreadPriority");
   *isRealTime = rt;
   return ret;
 }
@@ -37,14 +34,14 @@
   int32_t status = 0;
   auto native = thread.native_handle();
   auto ret = HAL_SetThreadPriority(&native, realTime, priority, &status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetThreadPriority");
   return ret;
 }
 
 bool SetCurrentThreadPriority(bool realTime, int priority) {
   int32_t status = 0;
   auto ret = HAL_SetCurrentThreadPriority(realTime, priority, &status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "SetCurrentThreadPriority");
   return ret;
 }
 
diff --git a/wpilibc/src/main/native/cpp/TimedRobot.cpp b/wpilibc/src/main/native/cpp/TimedRobot.cpp
index 74e658a..93e6698 100644
--- a/wpilibc/src/main/native/cpp/TimedRobot.cpp
+++ b/wpilibc/src/main/native/cpp/TimedRobot.cpp
@@ -1,23 +1,20 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/TimedRobot.h"
 
 #include <stdint.h>
 
+#include <cstdio>
 #include <utility>
 
 #include <hal/DriverStation.h>
 #include <hal/FRCUsageReporting.h>
 #include <hal/Notifier.h>
 
+#include "frc/Errors.h"
 #include "frc/Timer.h"
-#include "frc/Utility.h"
-#include "frc/WPIErrors.h"
 
 using namespace frc;
 
@@ -29,6 +26,7 @@
   }
 
   // Tell the DS that the robot is ready to be enabled
+  std::puts("\n********** Robot program startup complete **********");
   HAL_ObserveUserProgramStarting();
 
   // Loop forever, calling the appropriate mode-dependent function
@@ -42,10 +40,12 @@
     HAL_UpdateNotifierAlarm(
         m_notifier, static_cast<uint64_t>(callback.expirationTime * 1e6),
         &status);
-    wpi_setHALError(status);
+    FRC_CheckErrorStatus(status, "{}", "UpdateNotifierAlarm");
 
     uint64_t curTime = HAL_WaitForNotifierAlarm(m_notifier, &status);
-    if (curTime == 0 || status != 0) break;
+    if (curTime == 0 || status != 0) {
+      break;
+    }
 
     callback.func();
 
@@ -70,19 +70,15 @@
   HAL_StopNotifier(m_notifier, &status);
 }
 
-units::second_t TimedRobot::GetPeriod() const {
-  return units::second_t(m_period);
-}
-
 TimedRobot::TimedRobot(double period) : TimedRobot(units::second_t(period)) {}
 
 TimedRobot::TimedRobot(units::second_t period) : IterativeRobotBase(period) {
-  m_startTime = frc2::Timer::GetFPGATimestamp();
+  m_startTime = Timer::GetFPGATimestamp();
   AddPeriodic([=] { LoopFunc(); }, period);
 
   int32_t status = 0;
   m_notifier = HAL_InitializeNotifier(&status);
-  wpi_setHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "InitializeNotifier");
   HAL_SetNotifierName(m_notifier, "TimedRobot", &status);
 
   HAL_Report(HALUsageReporting::kResourceType_Framework,
@@ -93,7 +89,7 @@
   int32_t status = 0;
 
   HAL_StopNotifier(m_notifier, &status);
-  wpi_setHALError(status);
+  FRC_ReportError(status, "{}", "StopNotifier");
 
   HAL_CleanNotifier(m_notifier, &status);
 }
diff --git a/wpilibc/src/main/native/cpp/Timer.cpp b/wpilibc/src/main/native/cpp/Timer.cpp
index b428e03..bbd2262 100644
--- a/wpilibc/src/main/native/cpp/Timer.cpp
+++ b/wpilibc/src/main/native/cpp/Timer.cpp
@@ -1,42 +1,90 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Timer.h"
 
-#include <units/time.h>
+#include <chrono>
+#include <thread>
+
+#include "frc/DriverStation.h"
+#include "frc/RobotController.h"
 
 namespace frc {
 
-void Wait(double seconds) { frc2::Wait(units::second_t(seconds)); }
+void Wait(units::second_t seconds) {
+  std::this_thread::sleep_for(std::chrono::duration<double>(seconds.value()));
+}
 
-double GetTime() { return frc2::GetTime().to<double>(); }
+units::second_t GetTime() {
+  using std::chrono::duration;
+  using std::chrono::duration_cast;
+  using std::chrono::system_clock;
+
+  return units::second_t(
+      duration_cast<duration<double>>(system_clock::now().time_since_epoch())
+          .count());
+}
 
 }  // namespace frc
 
 using namespace frc;
 
-Timer::Timer() { Reset(); }
-
-double Timer::Get() const { return m_timer.Get().to<double>(); }
-
-void Timer::Reset() { m_timer.Reset(); }
-
-void Timer::Start() { m_timer.Start(); }
-
-void Timer::Stop() { m_timer.Stop(); }
-
-bool Timer::HasPeriodPassed(double period) {
-  return m_timer.HasPeriodPassed(units::second_t(period));
+Timer::Timer() {
+  Reset();
 }
 
-double Timer::GetFPGATimestamp() {
-  return frc2::Timer::GetFPGATimestamp().to<double>();
+units::second_t Timer::Get() const {
+  if (m_running) {
+    return (GetFPGATimestamp() - m_startTime) + m_accumulatedTime;
+  } else {
+    return m_accumulatedTime;
+  }
 }
 
-double Timer::GetMatchTime() {
-  return frc2::Timer::GetMatchTime().to<double>();
+void Timer::Reset() {
+  m_accumulatedTime = 0_s;
+  m_startTime = GetFPGATimestamp();
+}
+
+void Timer::Start() {
+  if (!m_running) {
+    m_startTime = GetFPGATimestamp();
+    m_running = true;
+  }
+}
+
+void Timer::Stop() {
+  if (m_running) {
+    m_accumulatedTime = Get();
+    m_running = false;
+  }
+}
+
+bool Timer::HasElapsed(units::second_t period) const {
+  return Get() >= period;
+}
+
+bool Timer::HasPeriodPassed(units::second_t period) {
+  return AdvanceIfElapsed(period);
+}
+
+bool Timer::AdvanceIfElapsed(units::second_t period) {
+  if (Get() >= period) {
+    // Advance the start time by the period.
+    m_startTime += period;
+    // Don't set it to the current time... we want to avoid drift.
+    return true;
+  } else {
+    return false;
+  }
+}
+
+units::second_t Timer::GetFPGATimestamp() {
+  // FPGA returns the timestamp in microseconds
+  return units::second_t(frc::RobotController::GetFPGATime() * 1.0e-6);
+}
+
+units::second_t Timer::GetMatchTime() {
+  return units::second_t(frc::DriverStation::GetMatchTime());
 }
diff --git a/wpilibc/src/main/native/cpp/TimesliceRobot.cpp b/wpilibc/src/main/native/cpp/TimesliceRobot.cpp
new file mode 100644
index 0000000..d212c10
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/TimesliceRobot.cpp
@@ -0,0 +1,28 @@
+// 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.
+
+#include "frc/TimesliceRobot.h"
+
+#include "frc/Errors.h"
+#include "frc/fmt/Units.h"
+
+using namespace frc;
+
+TimesliceRobot::TimesliceRobot(units::second_t robotPeriodicAllocation,
+                               units::second_t controllerPeriod)
+    : m_nextOffset{robotPeriodicAllocation},
+      m_controllerPeriod{controllerPeriod} {}
+
+void TimesliceRobot::Schedule(std::function<void()> func,
+                              units::second_t allocation) {
+  if (m_nextOffset + allocation > m_controllerPeriod) {
+    throw FRC_MakeError(err::Error,
+                        "Function scheduled at offset {} with allocation {} "
+                        "exceeded controller period of {}\n",
+                        m_nextOffset, allocation, m_controllerPeriod);
+  }
+
+  AddPeriodic(func, m_controllerPeriod, m_nextOffset);
+  m_nextOffset += allocation;
+}
diff --git a/wpilibc/src/main/native/cpp/Tracer.cpp b/wpilibc/src/main/native/cpp/Tracer.cpp
index af5177a..1375411 100644
--- a/wpilibc/src/main/native/cpp/Tracer.cpp
+++ b/wpilibc/src/main/native/cpp/Tracer.cpp
@@ -1,30 +1,31 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Tracer.h"
 
-#include <wpi/Format.h>
+#include <fmt/format.h>
 #include <wpi/SmallString.h>
 #include <wpi/raw_ostream.h>
 
-#include "frc/DriverStation.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 
-Tracer::Tracer() { ResetTimer(); }
+Tracer::Tracer() {
+  ResetTimer();
+}
 
-void Tracer::ResetTimer() { m_startTime = hal::fpga_clock::now(); }
+void Tracer::ResetTimer() {
+  m_startTime = hal::fpga_clock::now();
+}
 
 void Tracer::ClearEpochs() {
   ResetTimer();
   m_epochs.clear();
 }
 
-void Tracer::AddEpoch(wpi::StringRef epochName) {
+void Tracer::AddEpoch(std::string_view epochName) {
   auto currentTime = hal::fpga_clock::now();
   m_epochs[epochName] = currentTime - m_startTime;
   m_startTime = currentTime;
@@ -34,7 +35,9 @@
   wpi::SmallString<128> buf;
   wpi::raw_svector_ostream os(buf);
   PrintEpochs(os);
-  if (!buf.empty()) DriverStation::ReportWarning(buf);
+  if (!buf.empty()) {
+    FRC_ReportError(warn::Warning, "{}", buf.c_str());
+  }
 }
 
 void Tracer::PrintEpochs(wpi::raw_ostream& os) {
@@ -45,11 +48,9 @@
   if (now - m_lastEpochsPrintTime > kMinPrintPeriod) {
     m_lastEpochsPrintTime = now;
     for (const auto& epoch : m_epochs) {
-      os << '\t' << epoch.getKey() << ": "
-         << wpi::format(
-                "%.6f",
-                duration_cast<microseconds>(epoch.getValue()).count() / 1.0e6)
-         << "s\n";
+      os << fmt::format(
+          "\t{}: {:.6f}s\n", epoch.getKey(),
+          duration_cast<microseconds>(epoch.getValue()).count() / 1.0e6);
     }
   }
 }
diff --git a/wpilibc/src/main/native/cpp/Ultrasonic.cpp b/wpilibc/src/main/native/cpp/Ultrasonic.cpp
index dbd0d13..02035dd 100644
--- a/wpilibc/src/main/native/cpp/Ultrasonic.cpp
+++ b/wpilibc/src/main/native/cpp/Ultrasonic.cpp
@@ -1,23 +1,21 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Ultrasonic.h"
 
-#include <hal/FRCUsageReporting.h>
+#include <utility>
 
-#include "frc/Base.h"
+#include <hal/FRCUsageReporting.h>
+#include <wpi/NullDeleter.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
+
 #include "frc/Counter.h"
 #include "frc/DigitalInput.h"
 #include "frc/DigitalOutput.h"
+#include "frc/Errors.h"
 #include "frc/Timer.h"
-#include "frc/Utility.h"
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
@@ -27,47 +25,40 @@
 std::vector<Ultrasonic*> Ultrasonic::m_sensors;
 std::thread Ultrasonic::m_thread;
 
-Ultrasonic::Ultrasonic(int pingChannel, int echoChannel, DistanceUnit units)
+Ultrasonic::Ultrasonic(int pingChannel, int echoChannel)
     : m_pingChannel(std::make_shared<DigitalOutput>(pingChannel)),
       m_echoChannel(std::make_shared<DigitalInput>(echoChannel)),
       m_counter(m_echoChannel) {
-  m_units = units;
   Initialize();
-  auto& registry = SendableRegistry::GetInstance();
-  registry.AddChild(this, m_pingChannel.get());
-  registry.AddChild(this, m_echoChannel.get());
+  wpi::SendableRegistry::AddChild(this, m_pingChannel.get());
+  wpi::SendableRegistry::AddChild(this, m_echoChannel.get());
 }
 
-Ultrasonic::Ultrasonic(DigitalOutput* pingChannel, DigitalInput* echoChannel,
-                       DistanceUnit units)
-    : m_pingChannel(pingChannel, NullDeleter<DigitalOutput>()),
-      m_echoChannel(echoChannel, NullDeleter<DigitalInput>()),
+Ultrasonic::Ultrasonic(DigitalOutput* pingChannel, DigitalInput* echoChannel)
+    : m_pingChannel(pingChannel, wpi::NullDeleter<DigitalOutput>()),
+      m_echoChannel(echoChannel, wpi::NullDeleter<DigitalInput>()),
       m_counter(m_echoChannel) {
-  if (pingChannel == nullptr || echoChannel == nullptr) {
-    wpi_setWPIError(NullParameter);
-    m_units = units;
-    return;
+  if (!pingChannel) {
+    throw FRC_MakeError(err::NullParameter, "{}", "pingChannel");
   }
-  m_units = units;
+  if (!echoChannel) {
+    throw FRC_MakeError(err::NullParameter, "{}", "echoChannel");
+  }
   Initialize();
 }
 
-Ultrasonic::Ultrasonic(DigitalOutput& pingChannel, DigitalInput& echoChannel,
-                       DistanceUnit units)
-    : m_pingChannel(&pingChannel, NullDeleter<DigitalOutput>()),
-      m_echoChannel(&echoChannel, NullDeleter<DigitalInput>()),
+Ultrasonic::Ultrasonic(DigitalOutput& pingChannel, DigitalInput& echoChannel)
+    : m_pingChannel(&pingChannel, wpi::NullDeleter<DigitalOutput>()),
+      m_echoChannel(&echoChannel, wpi::NullDeleter<DigitalInput>()),
       m_counter(m_echoChannel) {
-  m_units = units;
   Initialize();
 }
 
 Ultrasonic::Ultrasonic(std::shared_ptr<DigitalOutput> pingChannel,
-                       std::shared_ptr<DigitalInput> echoChannel,
-                       DistanceUnit units)
-    : m_pingChannel(pingChannel),
-      m_echoChannel(echoChannel),
+                       std::shared_ptr<DigitalInput> echoChannel)
+    : m_pingChannel(std::move(pingChannel)),
+      m_echoChannel(std::move(echoChannel)),
       m_counter(m_echoChannel) {
-  m_units = units;
   Initialize();
 }
 
@@ -89,8 +80,15 @@
   }
 }
 
+int Ultrasonic::GetEchoChannel() const {
+  return m_echoChannel->GetChannel();
+}
+
 void Ultrasonic::Ping() {
-  wpi_assert(!m_automaticEnabled);
+  if (m_automaticEnabled) {
+    throw FRC_MakeError(err::IncompatibleMode, "{}",
+                        "cannot call Ping() in automatic mode");
+  }
 
   // Reset the counter to zero (invalid data now)
   m_counter.Reset();
@@ -100,12 +98,16 @@
 }
 
 bool Ultrasonic::IsRangeValid() const {
-  if (m_simRangeValid) return m_simRangeValid.Get();
+  if (m_simRangeValid) {
+    return m_simRangeValid.Get();
+  }
   return m_counter.Get() > 1;
 }
 
 void Ultrasonic::SetAutomaticMode(bool enabling) {
-  if (enabling == m_automaticEnabled) return;  // ignore the case of no change
+  if (enabling == m_automaticEnabled) {
+    return;  // ignore the case of no change
+  }
 
   m_automaticEnabled = enabling;
 
@@ -138,48 +140,29 @@
   }
 }
 
-double Ultrasonic::GetRangeInches() const {
+units::meter_t Ultrasonic::GetRange() const {
   if (IsRangeValid()) {
-    if (m_simRange) return m_simRange.Get();
-    return m_counter.GetPeriod() * kSpeedOfSoundInchesPerSec / 2.0;
+    if (m_simRange) {
+      return units::meter_t{m_simRange.Get()};
+    }
+    return m_counter.GetPeriod() * kSpeedOfSound / 2.0;
   } else {
-    return 0;
+    return 0_m;
   }
 }
 
-double Ultrasonic::GetRangeMM() const { return GetRangeInches() * 25.4; }
-
-bool Ultrasonic::IsEnabled() const { return m_enabled; }
-
-void Ultrasonic::SetEnabled(bool enable) { m_enabled = enable; }
-
-void Ultrasonic::SetDistanceUnits(DistanceUnit units) { m_units = units; }
-
-Ultrasonic::DistanceUnit Ultrasonic::GetDistanceUnits() const {
-  return m_units;
+bool Ultrasonic::IsEnabled() const {
+  return m_enabled;
 }
 
-double Ultrasonic::PIDGet() {
-  switch (m_units) {
-    case Ultrasonic::kInches:
-      return GetRangeInches();
-    case Ultrasonic::kMilliMeters:
-      return GetRangeMM();
-    default:
-      return 0.0;
-  }
+void Ultrasonic::SetEnabled(bool enable) {
+  m_enabled = enable;
 }
 
-void Ultrasonic::SetPIDSourceType(PIDSourceType pidSource) {
-  if (wpi_assert(pidSource == PIDSourceType::kDisplacement)) {
-    m_pidSource = pidSource;
-  }
-}
-
-void Ultrasonic::InitSendable(SendableBuilder& builder) {
+void Ultrasonic::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("Ultrasonic");
   builder.AddDoubleProperty(
-      "Value", [=]() { return GetRangeInches(); }, nullptr);
+      "Value", [=] { return units::inch_t{GetRange()}.value(); }, nullptr);
 }
 
 void Ultrasonic::Initialize() {
@@ -196,7 +179,7 @@
   // Link this instance on the list
   m_sensors.emplace_back(this);
 
-  m_counter.SetMaxPeriod(1.0);
+  m_counter.SetMaxPeriod(1_s);
   m_counter.SetSemiPeriodMode(true);
   m_counter.Reset();
   m_enabled = true;  // Make it available for round robin scheduling
@@ -205,20 +188,21 @@
   static int instances = 0;
   instances++;
   HAL_Report(HALUsageReporting::kResourceType_Ultrasonic, instances);
-  SendableRegistry::GetInstance().AddLW(this, "Ultrasonic",
-                                        m_echoChannel->GetChannel());
+  wpi::SendableRegistry::AddLW(this, "Ultrasonic", m_echoChannel->GetChannel());
 }
 
 void Ultrasonic::UltrasonicChecker() {
   while (m_automaticEnabled) {
     for (auto& sensor : m_sensors) {
-      if (!m_automaticEnabled) break;
+      if (!m_automaticEnabled) {
+        break;
+      }
 
       if (sensor->IsEnabled()) {
         sensor->m_pingChannel->Pulse(kPingTime);  // do the ping
       }
 
-      Wait(0.1);  // wait for ping to return
+      Wait(100_ms);  // wait for ping to return
     }
   }
 }
diff --git a/wpilibc/src/main/native/cpp/Utility.cpp b/wpilibc/src/main/native/cpp/Utility.cpp
deleted file mode 100644
index 4b69cc8..0000000
--- a/wpilibc/src/main/native/cpp/Utility.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/Utility.h"
-
-#ifndef _WIN32
-#include <cxxabi.h>
-#include <execinfo.h>
-#endif
-
-#include <frc/Base.h>
-#include <hal/DriverStation.h>
-#include <wpi/Path.h>
-#include <wpi/SmallString.h>
-#include <wpi/StackTrace.h>
-#include <wpi/raw_ostream.h>
-
-using namespace frc;
-
-bool wpi_assert_impl(bool conditionValue, const wpi::Twine& conditionText,
-                     const wpi::Twine& message, wpi::StringRef fileName,
-                     int lineNumber, wpi::StringRef funcName) {
-  if (!conditionValue) {
-    wpi::SmallString<128> locBuf;
-    wpi::raw_svector_ostream locStream(locBuf);
-    locStream << funcName << " [" << wpi::sys::path::filename(fileName) << ":"
-              << lineNumber << "]";
-
-    wpi::SmallString<128> errorBuf;
-    wpi::raw_svector_ostream errorStream(errorBuf);
-
-    errorStream << "Assertion \"" << conditionText << "\" ";
-
-    if (message.isTriviallyEmpty() ||
-        (message.isSingleStringRef() && message.getSingleStringRef().empty())) {
-      errorStream << "failed.\n";
-    } else {
-      errorStream << "failed: " << message << "\n";
-    }
-
-    std::string stack = wpi::GetStackTrace(2);
-
-    // Print the error and send it to the DriverStation
-    HAL_SendError(1, 1, 0, errorBuf.c_str(), locBuf.c_str(), stack.c_str(), 1);
-  }
-
-  return conditionValue;
-}
-
-/**
- * Common error routines for wpi_assertEqual_impl and wpi_assertNotEqual_impl.
- *
- * This should not be called directly; it should only be used by
- * wpi_assertEqual_impl and wpi_assertNotEqual_impl.
- */
-void wpi_assertEqual_common_impl(const wpi::Twine& valueA,
-                                 const wpi::Twine& valueB,
-                                 const wpi::Twine& equalityType,
-                                 const wpi::Twine& message,
-                                 wpi::StringRef fileName, int lineNumber,
-                                 wpi::StringRef funcName) {
-  wpi::SmallString<128> locBuf;
-  wpi::raw_svector_ostream locStream(locBuf);
-  locStream << funcName << " [" << wpi::sys::path::filename(fileName) << ":"
-            << lineNumber << "]";
-
-  wpi::SmallString<128> errorBuf;
-  wpi::raw_svector_ostream errorStream(errorBuf);
-
-  errorStream << "Assertion \"" << valueA << " " << equalityType << " "
-              << valueB << "\" ";
-
-  if (message.isTriviallyEmpty() ||
-      (message.isSingleStringRef() && message.getSingleStringRef().empty())) {
-    errorStream << "failed.\n";
-  } else {
-    errorStream << "failed: " << message << "\n";
-  }
-
-  std::string trace = wpi::GetStackTrace(3);
-
-  // Print the error and send it to the DriverStation
-  HAL_SendError(1, 1, 0, errorBuf.c_str(), locBuf.c_str(), trace.c_str(), 1);
-}
-
-bool wpi_assertEqual_impl(int valueA, int valueB,
-                          const wpi::Twine& valueAString,
-                          const wpi::Twine& valueBString,
-                          const wpi::Twine& message, wpi::StringRef fileName,
-                          int lineNumber, wpi::StringRef funcName) {
-  if (!(valueA == valueB)) {
-    wpi_assertEqual_common_impl(valueAString, valueBString, "==", message,
-                                fileName, lineNumber, funcName);
-  }
-  return valueA == valueB;
-}
-
-bool wpi_assertNotEqual_impl(int valueA, int valueB,
-                             const wpi::Twine& valueAString,
-                             const wpi::Twine& valueBString,
-                             const wpi::Twine& message, wpi::StringRef fileName,
-                             int lineNumber, wpi::StringRef funcName) {
-  if (!(valueA != valueB)) {
-    wpi_assertEqual_common_impl(valueAString, valueBString, "!=", message,
-                                fileName, lineNumber, funcName);
-  }
-  return valueA != valueB;
-}
diff --git a/wpilibc/src/main/native/cpp/Victor.cpp b/wpilibc/src/main/native/cpp/Victor.cpp
deleted file mode 100644
index bce1913..0000000
--- a/wpilibc/src/main/native/cpp/Victor.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/Victor.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-Victor::Victor(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.027, 1.525, 1.507, 1.49, 1.026);
-  SetPeriodMultiplier(kPeriodMultiplier_2X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_Victor, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "Victor", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/VictorSP.cpp b/wpilibc/src/main/native/cpp/VictorSP.cpp
deleted file mode 100644
index 221777d..0000000
--- a/wpilibc/src/main/native/cpp/VictorSP.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/VictorSP.h"
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-VictorSP::VictorSP(int channel) : PWMSpeedController(channel) {
-  SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
-  SetPeriodMultiplier(kPeriodMultiplier_1X);
-  SetSpeed(0.0);
-  SetZeroLatch();
-
-  HAL_Report(HALUsageReporting::kResourceType_VictorSP, GetChannel() + 1);
-  SendableRegistry::GetInstance().SetName(this, "VictorSP", GetChannel());
-}
diff --git a/wpilibc/src/main/native/cpp/Watchdog.cpp b/wpilibc/src/main/native/cpp/Watchdog.cpp
index 81c7cc8..854f9e9 100644
--- a/wpilibc/src/main/native/cpp/Watchdog.cpp
+++ b/wpilibc/src/main/native/cpp/Watchdog.cpp
@@ -1,22 +1,20 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/Watchdog.h"
 
 #include <atomic>
+#include <thread>
+#include <utility>
 
+#include <fmt/format.h>
 #include <hal/Notifier.h>
-#include <wpi/Format.h>
-#include <wpi/SmallString.h>
+#include <wpi/mutex.h>
 #include <wpi/priority_queue.h>
-#include <wpi/raw_ostream.h>
 
-#include "frc/DriverStation.h"
-#include "frc2/Timer.h"
+#include "frc/Errors.h"
+#include "frc/Timer.h"
 
 using namespace frc;
 
@@ -49,7 +47,7 @@
 Watchdog::Impl::Impl() {
   int32_t status = 0;
   m_notifier = HAL_InitializeNotifier(&status);
-  wpi_setGlobalHALError(status);
+  FRC_CheckErrorStatus(status, "{}", "starting watchdog notifier");
   HAL_SetNotifierName(m_notifier, "Watchdog", &status);
 
   m_thread = std::thread([=] { Main(); });
@@ -60,10 +58,12 @@
   // atomically set handle to 0, then clean
   HAL_NotifierHandle handle = m_notifier.exchange(0);
   HAL_StopNotifier(handle, &status);
-  wpi_setGlobalHALError(status);
+  FRC_ReportError(status, "{}", "stopping watchdog notifier");
 
   // Join the thread to ensure the handler has exited.
-  if (m_thread.joinable()) m_thread.join();
+  if (m_thread.joinable()) {
+    m_thread.join();
+  }
 
   HAL_CleanNotifier(handle, &status);
 }
@@ -72,29 +72,38 @@
   int32_t status = 0;
   // Return if we are being destructed, or were not created successfully
   auto notifier = m_notifier.load();
-  if (notifier == 0) return;
-  if (m_watchdogs.empty())
+  if (notifier == 0) {
+    return;
+  }
+  if (m_watchdogs.empty()) {
     HAL_CancelNotifierAlarm(notifier, &status);
-  else
+  } else {
     HAL_UpdateNotifierAlarm(
         notifier,
-        static_cast<uint64_t>(m_watchdogs.top()->m_expirationTime.to<double>() *
+        static_cast<uint64_t>(m_watchdogs.top()->m_expirationTime.value() *
                               1e6),
         &status);
-  wpi_setGlobalHALError(status);
+  }
+  FRC_CheckErrorStatus(status, "{}", "updating watchdog notifier alarm");
 }
 
 void Watchdog::Impl::Main() {
   for (;;) {
     int32_t status = 0;
     HAL_NotifierHandle notifier = m_notifier.load();
-    if (notifier == 0) break;
+    if (notifier == 0) {
+      break;
+    }
     uint64_t curTime = HAL_WaitForNotifierAlarm(notifier, &status);
-    if (curTime == 0 || status != 0) break;
+    if (curTime == 0 || status != 0) {
+      break;
+    }
 
     std::unique_lock lock(m_mutex);
 
-    if (m_watchdogs.empty()) continue;
+    if (m_watchdogs.empty()) {
+      continue;
+    }
 
     // If the condition variable timed out, that means a Watchdog timeout
     // has occurred, so call its timeout function.
@@ -104,11 +113,8 @@
     if (now - watchdog->m_lastTimeoutPrintTime > kMinPrintPeriod) {
       watchdog->m_lastTimeoutPrintTime = now;
       if (!watchdog->m_suppressTimeoutMessage) {
-        wpi::SmallString<128> buf;
-        wpi::raw_svector_ostream err(buf);
-        err << "Watchdog not fed within "
-            << wpi::format("%.6f", watchdog->m_timeout.to<double>()) << "s\n";
-        frc::DriverStation::ReportWarning(err.str());
+        FRC_ReportError(warn::Warning, "Watchdog not fed within {:.6f}s",
+                        watchdog->m_timeout.value());
       }
     }
 
@@ -125,15 +131,20 @@
   }
 }
 
-Watchdog::Watchdog(double timeout, std::function<void()> callback)
-    : Watchdog(units::second_t{timeout}, callback) {}
-
 Watchdog::Watchdog(units::second_t timeout, std::function<void()> callback)
-    : m_timeout(timeout), m_callback(callback), m_impl(GetImpl()) {}
+    : m_timeout(timeout), m_callback(std::move(callback)), m_impl(GetImpl()) {}
 
-Watchdog::~Watchdog() { Disable(); }
+Watchdog::~Watchdog() {
+  try {
+    Disable();
+  } catch (const RuntimeError& e) {
+    e.Report();
+  }
+}
 
-Watchdog::Watchdog(Watchdog&& rhs) { *this = std::move(rhs); }
+Watchdog::Watchdog(Watchdog&& rhs) {
+  *this = std::move(rhs);
+}
 
 Watchdog& Watchdog::operator=(Watchdog&& rhs) {
   m_impl = rhs.m_impl;
@@ -153,16 +164,12 @@
   return *this;
 }
 
-double Watchdog::GetTime() const {
-  return (frc2::Timer::GetFPGATimestamp() - m_startTime).to<double>();
-}
-
-void Watchdog::SetTimeout(double timeout) {
-  SetTimeout(units::second_t{timeout});
+units::second_t Watchdog::GetTime() const {
+  return Timer::GetFPGATimestamp() - m_startTime;
 }
 
 void Watchdog::SetTimeout(units::second_t timeout) {
-  m_startTime = frc2::Timer::GetFPGATimestamp();
+  m_startTime = Timer::GetFPGATimestamp();
   m_tracer.ClearEpochs();
 
   std::scoped_lock lock(m_impl->m_mutex);
@@ -175,9 +182,9 @@
   m_impl->UpdateAlarm();
 }
 
-double Watchdog::GetTimeout() const {
+units::second_t Watchdog::GetTimeout() const {
   std::scoped_lock lock(m_impl->m_mutex);
-  return m_timeout.to<double>();
+  return m_timeout;
 }
 
 bool Watchdog::IsExpired() const {
@@ -185,16 +192,20 @@
   return m_isExpired;
 }
 
-void Watchdog::AddEpoch(wpi::StringRef epochName) {
+void Watchdog::AddEpoch(std::string_view epochName) {
   m_tracer.AddEpoch(epochName);
 }
 
-void Watchdog::PrintEpochs() { m_tracer.PrintEpochs(); }
+void Watchdog::PrintEpochs() {
+  m_tracer.PrintEpochs();
+}
 
-void Watchdog::Reset() { Enable(); }
+void Watchdog::Reset() {
+  Enable();
+}
 
 void Watchdog::Enable() {
-  m_startTime = frc2::Timer::GetFPGATimestamp();
+  m_startTime = Timer::GetFPGATimestamp();
   m_tracer.ClearEpochs();
 
   std::scoped_lock lock(m_impl->m_mutex);
diff --git a/wpilibc/src/main/native/cpp/XboxController.cpp b/wpilibc/src/main/native/cpp/XboxController.cpp
index de77624..a08225b 100644
--- a/wpilibc/src/main/native/cpp/XboxController.cpp
+++ b/wpilibc/src/main/native/cpp/XboxController.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2016-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/XboxController.h"
 
@@ -15,146 +12,146 @@
   HAL_Report(HALUsageReporting::kResourceType_XboxController, port + 1);
 }
 
-double XboxController::GetX(JoystickHand hand) const {
-  if (hand == kLeftHand) {
-    return GetRawAxis(static_cast<int>(Axis::kLeftX));
-  } else {
-    return GetRawAxis(static_cast<int>(Axis::kRightX));
-  }
+double XboxController::GetLeftX() const {
+  return GetRawAxis(Axis::kLeftX);
 }
 
-double XboxController::GetY(JoystickHand hand) const {
-  if (hand == kLeftHand) {
-    return GetRawAxis(static_cast<int>(Axis::kLeftY));
-  } else {
-    return GetRawAxis(static_cast<int>(Axis::kRightY));
-  }
+double XboxController::GetRightX() const {
+  return GetRawAxis(Axis::kRightX);
 }
 
-double XboxController::GetTriggerAxis(JoystickHand hand) const {
-  if (hand == kLeftHand) {
-    return GetRawAxis(static_cast<int>(Axis::kLeftTrigger));
-  } else {
-    return GetRawAxis(static_cast<int>(Axis::kRightTrigger));
-  }
+double XboxController::GetLeftY() const {
+  return GetRawAxis(Axis::kLeftY);
 }
 
-bool XboxController::GetBumper(JoystickHand hand) const {
-  if (hand == kLeftHand) {
-    return GetRawButton(static_cast<int>(Button::kBumperLeft));
-  } else {
-    return GetRawButton(static_cast<int>(Button::kBumperRight));
-  }
+double XboxController::GetRightY() const {
+  return GetRawAxis(Axis::kRightY);
 }
 
-bool XboxController::GetBumperPressed(JoystickHand hand) {
-  if (hand == kLeftHand) {
-    return GetRawButtonPressed(static_cast<int>(Button::kBumperLeft));
-  } else {
-    return GetRawButtonPressed(static_cast<int>(Button::kBumperRight));
-  }
+double XboxController::GetLeftTriggerAxis() const {
+  return GetRawAxis(Axis::kLeftTrigger);
 }
 
-bool XboxController::GetBumperReleased(JoystickHand hand) {
-  if (hand == kLeftHand) {
-    return GetRawButtonReleased(static_cast<int>(Button::kBumperLeft));
-  } else {
-    return GetRawButtonReleased(static_cast<int>(Button::kBumperRight));
-  }
+double XboxController::GetRightTriggerAxis() const {
+  return GetRawAxis(Axis::kRightTrigger);
 }
 
-bool XboxController::GetStickButton(JoystickHand hand) const {
-  if (hand == kLeftHand) {
-    return GetRawButton(static_cast<int>(Button::kStickLeft));
-  } else {
-    return GetRawButton(static_cast<int>(Button::kStickRight));
-  }
+bool XboxController::GetLeftBumper() const {
+  return GetRawButton(Button::kLeftBumper);
 }
 
-bool XboxController::GetStickButtonPressed(JoystickHand hand) {
-  if (hand == kLeftHand) {
-    return GetRawButtonPressed(static_cast<int>(Button::kStickLeft));
-  } else {
-    return GetRawButtonPressed(static_cast<int>(Button::kStickRight));
-  }
+bool XboxController::GetRightBumper() const {
+  return GetRawButton(Button::kRightBumper);
 }
 
-bool XboxController::GetStickButtonReleased(JoystickHand hand) {
-  if (hand == kLeftHand) {
-    return GetRawButtonReleased(static_cast<int>(Button::kStickLeft));
-  } else {
-    return GetRawButtonReleased(static_cast<int>(Button::kStickRight));
-  }
+bool XboxController::GetLeftBumperPressed() {
+  return GetRawButtonPressed(Button::kLeftBumper);
+}
+
+bool XboxController::GetRightBumperPressed() {
+  return GetRawButtonPressed(Button::kRightBumper);
+}
+
+bool XboxController::GetLeftBumperReleased() {
+  return GetRawButtonReleased(Button::kLeftBumper);
+}
+
+bool XboxController::GetRightBumperReleased() {
+  return GetRawButtonReleased(Button::kRightBumper);
+}
+
+bool XboxController::GetLeftStickButton() const {
+  return GetRawButton(Button::kLeftStick);
+}
+
+bool XboxController::GetRightStickButton() const {
+  return GetRawButton(Button::kRightStick);
+}
+
+bool XboxController::GetLeftStickButtonPressed() {
+  return GetRawButtonPressed(Button::kLeftStick);
+}
+
+bool XboxController::GetRightStickButtonPressed() {
+  return GetRawButtonPressed(Button::kRightStick);
+}
+
+bool XboxController::GetLeftStickButtonReleased() {
+  return GetRawButtonReleased(Button::kLeftStick);
+}
+
+bool XboxController::GetRightStickButtonReleased() {
+  return GetRawButtonReleased(Button::kRightStick);
 }
 
 bool XboxController::GetAButton() const {
-  return GetRawButton(static_cast<int>(Button::kA));
+  return GetRawButton(Button::kA);
 }
 
 bool XboxController::GetAButtonPressed() {
-  return GetRawButtonPressed(static_cast<int>(Button::kA));
+  return GetRawButtonPressed(Button::kA);
 }
 
 bool XboxController::GetAButtonReleased() {
-  return GetRawButtonReleased(static_cast<int>(Button::kA));
+  return GetRawButtonReleased(Button::kA);
 }
 
 bool XboxController::GetBButton() const {
-  return GetRawButton(static_cast<int>(Button::kB));
+  return GetRawButton(Button::kB);
 }
 
 bool XboxController::GetBButtonPressed() {
-  return GetRawButtonPressed(static_cast<int>(Button::kB));
+  return GetRawButtonPressed(Button::kB);
 }
 
 bool XboxController::GetBButtonReleased() {
-  return GetRawButtonReleased(static_cast<int>(Button::kB));
+  return GetRawButtonReleased(Button::kB);
 }
 
 bool XboxController::GetXButton() const {
-  return GetRawButton(static_cast<int>(Button::kX));
+  return GetRawButton(Button::kX);
 }
 
 bool XboxController::GetXButtonPressed() {
-  return GetRawButtonPressed(static_cast<int>(Button::kX));
+  return GetRawButtonPressed(Button::kX);
 }
 
 bool XboxController::GetXButtonReleased() {
-  return GetRawButtonReleased(static_cast<int>(Button::kX));
+  return GetRawButtonReleased(Button::kX);
 }
 
 bool XboxController::GetYButton() const {
-  return GetRawButton(static_cast<int>(Button::kY));
+  return GetRawButton(Button::kY);
 }
 
 bool XboxController::GetYButtonPressed() {
-  return GetRawButtonPressed(static_cast<int>(Button::kY));
+  return GetRawButtonPressed(Button::kY);
 }
 
 bool XboxController::GetYButtonReleased() {
-  return GetRawButtonReleased(static_cast<int>(Button::kY));
+  return GetRawButtonReleased(Button::kY);
 }
 
 bool XboxController::GetBackButton() const {
-  return GetRawButton(static_cast<int>(Button::kBack));
+  return GetRawButton(Button::kBack);
 }
 
 bool XboxController::GetBackButtonPressed() {
-  return GetRawButtonPressed(static_cast<int>(Button::kBack));
+  return GetRawButtonPressed(Button::kBack);
 }
 
 bool XboxController::GetBackButtonReleased() {
-  return GetRawButtonReleased(static_cast<int>(Button::kBack));
+  return GetRawButtonReleased(Button::kBack);
 }
 
 bool XboxController::GetStartButton() const {
-  return GetRawButton(static_cast<int>(Button::kStart));
+  return GetRawButton(Button::kStart);
 }
 
 bool XboxController::GetStartButtonPressed() {
-  return GetRawButtonPressed(static_cast<int>(Button::kStart));
+  return GetRawButtonPressed(Button::kStart);
 }
 
 bool XboxController::GetStartButtonReleased() {
-  return GetRawButtonReleased(static_cast<int>(Button::kStart));
+  return GetRawButtonReleased(Button::kStart);
 }
diff --git a/wpilibc/src/main/native/cpp/controller/HolonomicDriveController.cpp b/wpilibc/src/main/native/cpp/controller/HolonomicDriveController.cpp
deleted file mode 100644
index 7482e22..0000000
--- a/wpilibc/src/main/native/cpp/controller/HolonomicDriveController.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/controller/HolonomicDriveController.h"
-
-#include <units/angular_velocity.h>
-
-using namespace frc;
-
-HolonomicDriveController::HolonomicDriveController(
-    const frc2::PIDController& xController,
-    const frc2::PIDController& yController,
-    const ProfiledPIDController<units::radian>& thetaController)
-    : m_xController(xController),
-      m_yController(yController),
-      m_thetaController(thetaController) {}
-
-bool HolonomicDriveController::AtReference() const {
-  const auto& eTranslate = m_poseError.Translation();
-  const auto& eRotate = m_poseError.Rotation();
-  const auto& tolTranslate = m_poseTolerance.Translation();
-  const auto& tolRotate = m_poseTolerance.Rotation();
-  return units::math::abs(eTranslate.X()) < tolTranslate.X() &&
-         units::math::abs(eTranslate.Y()) < tolTranslate.Y() &&
-         units::math::abs(eRotate.Radians()) < tolRotate.Radians();
-}
-
-void HolonomicDriveController::SetTolerance(const Pose2d& tolerance) {
-  m_poseTolerance = tolerance;
-}
-
-ChassisSpeeds HolonomicDriveController::Calculate(
-    const Pose2d& currentPose, const Pose2d& poseRef,
-    units::meters_per_second_t linearVelocityRef, const Rotation2d& angleRef) {
-  // Calculate feedforward velocities (field-relative)
-  auto xFF = linearVelocityRef * poseRef.Rotation().Cos();
-  auto yFF = linearVelocityRef * poseRef.Rotation().Sin();
-  auto thetaFF = units::radians_per_second_t(m_thetaController.Calculate(
-      currentPose.Rotation().Radians(), angleRef.Radians()));
-
-  m_poseError = poseRef.RelativeTo(currentPose);
-
-  if (!m_enabled) {
-    return ChassisSpeeds::FromFieldRelativeSpeeds(xFF, yFF, thetaFF,
-                                                  currentPose.Rotation());
-  }
-
-  // Calculate feedback velocities (based on position error).
-  auto xFeedback = units::meters_per_second_t(m_xController.Calculate(
-      currentPose.X().to<double>(), poseRef.X().to<double>()));
-  auto yFeedback = units::meters_per_second_t(m_yController.Calculate(
-      currentPose.Y().to<double>(), poseRef.Y().to<double>()));
-
-  // Return next output.
-  return ChassisSpeeds::FromFieldRelativeSpeeds(
-      xFF + xFeedback, yFF + yFeedback, thetaFF, currentPose.Rotation());
-}
-
-ChassisSpeeds HolonomicDriveController::Calculate(
-    const Pose2d& currentPose, const Trajectory::State& desiredState,
-    const Rotation2d& angleRef) {
-  return Calculate(currentPose, desiredState.pose, desiredState.velocity,
-                   angleRef);
-}
-
-void HolonomicDriveController::SetEnabled(bool enabled) { m_enabled = enabled; }
diff --git a/wpilibc/src/main/native/cpp/controller/PIDController.cpp b/wpilibc/src/main/native/cpp/controller/PIDController.cpp
deleted file mode 100644
index 7b9b623..0000000
--- a/wpilibc/src/main/native/cpp/controller/PIDController.cpp
+++ /dev/null
@@ -1,131 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/controller/PIDController.h"
-
-#include <algorithm>
-#include <cmath>
-
-#include <hal/FRCUsageReporting.h>
-
-#include "frc/controller/ControllerUtil.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc2;
-
-PIDController::PIDController(double Kp, double Ki, double Kd,
-                             units::second_t period)
-    : m_Kp(Kp), m_Ki(Ki), m_Kd(Kd), m_period(period) {
-  static int instances = 0;
-  instances++;
-  HAL_Report(HALUsageReporting::kResourceType_PIDController2, instances);
-  frc::SendableRegistry::GetInstance().Add(this, "PIDController", instances);
-}
-
-void PIDController::SetPID(double Kp, double Ki, double Kd) {
-  m_Kp = Kp;
-  m_Ki = Ki;
-  m_Kd = Kd;
-}
-
-void PIDController::SetP(double Kp) { m_Kp = Kp; }
-
-void PIDController::SetI(double Ki) { m_Ki = Ki; }
-
-void PIDController::SetD(double Kd) { m_Kd = Kd; }
-
-double PIDController::GetP() const { return m_Kp; }
-
-double PIDController::GetI() const { return m_Ki; }
-
-double PIDController::GetD() const { return m_Kd; }
-
-units::second_t PIDController::GetPeriod() const {
-  return units::second_t(m_period);
-}
-
-void PIDController::SetSetpoint(double setpoint) { m_setpoint = setpoint; }
-
-double PIDController::GetSetpoint() const { return m_setpoint; }
-
-bool PIDController::AtSetpoint() const {
-  return std::abs(m_positionError) < m_positionTolerance &&
-         std::abs(m_velocityError) < m_velocityTolerance;
-}
-
-void PIDController::EnableContinuousInput(double minimumInput,
-                                          double maximumInput) {
-  m_continuous = true;
-  m_minimumInput = minimumInput;
-  m_maximumInput = maximumInput;
-}
-
-void PIDController::DisableContinuousInput() { m_continuous = false; }
-
-bool PIDController::IsContinuousInputEnabled() const { return m_continuous; }
-
-void PIDController::SetIntegratorRange(double minimumIntegral,
-                                       double maximumIntegral) {
-  m_minimumIntegral = minimumIntegral;
-  m_maximumIntegral = maximumIntegral;
-}
-
-void PIDController::SetTolerance(double positionTolerance,
-                                 double velocityTolerance) {
-  m_positionTolerance = positionTolerance;
-  m_velocityTolerance = velocityTolerance;
-}
-
-double PIDController::GetPositionError() const { return m_positionError; }
-
-double PIDController::GetVelocityError() const { return m_velocityError; }
-
-double PIDController::Calculate(double measurement) {
-  m_prevError = m_positionError;
-
-  if (m_continuous) {
-    m_positionError = frc::GetModulusError<double>(
-        m_setpoint, measurement, m_minimumInput, m_maximumInput);
-  } else {
-    m_positionError = m_setpoint - measurement;
-  }
-
-  m_velocityError = (m_positionError - m_prevError) / m_period.to<double>();
-
-  if (m_Ki != 0) {
-    m_totalError =
-        std::clamp(m_totalError + m_positionError * m_period.to<double>(),
-                   m_minimumIntegral / m_Ki, m_maximumIntegral / m_Ki);
-  }
-
-  return m_Kp * m_positionError + m_Ki * m_totalError + m_Kd * m_velocityError;
-}
-
-double PIDController::Calculate(double measurement, double setpoint) {
-  // Set setpoint to provided value
-  SetSetpoint(setpoint);
-  return Calculate(measurement);
-}
-
-void PIDController::Reset() {
-  m_prevError = 0;
-  m_totalError = 0;
-}
-
-void PIDController::InitSendable(frc::SendableBuilder& builder) {
-  builder.SetSmartDashboardType("PIDController");
-  builder.AddDoubleProperty(
-      "p", [this] { return GetP(); }, [this](double value) { SetP(value); });
-  builder.AddDoubleProperty(
-      "i", [this] { return GetI(); }, [this](double value) { SetI(value); });
-  builder.AddDoubleProperty(
-      "d", [this] { return GetD(); }, [this](double value) { SetD(value); });
-  builder.AddDoubleProperty(
-      "setpoint", [this] { return GetSetpoint(); },
-      [this](double value) { SetSetpoint(value); });
-}
diff --git a/wpilibc/src/main/native/cpp/controller/ProfiledPIDController.cpp b/wpilibc/src/main/native/cpp/controller/ProfiledPIDController.cpp
deleted file mode 100644
index a0fffc6..0000000
--- a/wpilibc/src/main/native/cpp/controller/ProfiledPIDController.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/controller/ProfiledPIDController.h"
-
-#include <hal/FRCUsageReporting.h>
-
-void frc::detail::ReportProfiledPIDController() {
-  static int instances = 0;
-  ++instances;
-  HAL_Report(HALUsageReporting::kResourceType_ProfiledPIDController, instances);
-}
diff --git a/wpilibc/src/main/native/cpp/controller/RamseteController.cpp b/wpilibc/src/main/native/cpp/controller/RamseteController.cpp
deleted file mode 100644
index aad6937..0000000
--- a/wpilibc/src/main/native/cpp/controller/RamseteController.cpp
+++ /dev/null
@@ -1,78 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/controller/RamseteController.h"
-
-#include <cmath>
-
-#include <units/math.h>
-
-using namespace frc;
-
-/**
- * Returns sin(x) / x.
- *
- * @param x Value of which to take sinc(x).
- */
-static double Sinc(double x) {
-  if (std::abs(x) < 1e-9) {
-    return 1.0 - 1.0 / 6.0 * x * x;
-  } else {
-    return std::sin(x) / x;
-  }
-}
-
-RamseteController::RamseteController(double b, double zeta)
-    : m_b{b}, m_zeta{zeta} {}
-
-bool RamseteController::AtReference() const {
-  const auto& eTranslate = m_poseError.Translation();
-  const auto& eRotate = m_poseError.Rotation();
-  const auto& tolTranslate = m_poseTolerance.Translation();
-  const auto& tolRotate = m_poseTolerance.Rotation();
-  return units::math::abs(eTranslate.X()) < tolTranslate.X() &&
-         units::math::abs(eTranslate.Y()) < tolTranslate.Y() &&
-         units::math::abs(eRotate.Radians()) < tolRotate.Radians();
-}
-
-void RamseteController::SetTolerance(const Pose2d& poseTolerance) {
-  m_poseTolerance = poseTolerance;
-}
-
-ChassisSpeeds RamseteController::Calculate(
-    const Pose2d& currentPose, const Pose2d& poseRef,
-    units::meters_per_second_t linearVelocityRef,
-    units::radians_per_second_t angularVelocityRef) {
-  if (!m_enabled) {
-    return ChassisSpeeds{linearVelocityRef, 0_mps, angularVelocityRef};
-  }
-
-  m_poseError = poseRef.RelativeTo(currentPose);
-
-  // Aliases for equation readability
-  double eX = m_poseError.X().to<double>();
-  double eY = m_poseError.Y().to<double>();
-  double eTheta = m_poseError.Rotation().Radians().to<double>();
-  double vRef = linearVelocityRef.to<double>();
-  double omegaRef = angularVelocityRef.to<double>();
-
-  double k =
-      2.0 * m_zeta * std::sqrt(std::pow(omegaRef, 2) + m_b * std::pow(vRef, 2));
-
-  units::meters_per_second_t v{vRef * m_poseError.Rotation().Cos() + k * eX};
-  units::radians_per_second_t omega{omegaRef + k * eTheta +
-                                    m_b * vRef * Sinc(eTheta) * eY};
-  return ChassisSpeeds{v, 0_mps, omega};
-}
-
-ChassisSpeeds RamseteController::Calculate(
-    const Pose2d& currentPose, const Trajectory::State& desiredState) {
-  return Calculate(currentPose, desiredState.pose, desiredState.velocity,
-                   desiredState.velocity * desiredState.curvature);
-}
-
-void RamseteController::SetEnabled(bool enabled) { m_enabled = enabled; }
diff --git a/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp b/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp
index ce9aa05..b54b607 100644
--- a/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp
+++ b/wpilibc/src/main/native/cpp/drive/DifferentialDrive.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/drive/DifferentialDrive.h"
 
@@ -11,22 +8,30 @@
 #include <cmath>
 
 #include <hal/FRCUsageReporting.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/MathUtil.h"
 #include "frc/SpeedController.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
+#if defined(_MSC_VER)
+#pragma warning(disable : 4996)  // was declared deprecated
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 DifferentialDrive::DifferentialDrive(SpeedController& leftMotor,
                                      SpeedController& rightMotor)
     : m_leftMotor(&leftMotor), m_rightMotor(&rightMotor) {
-  auto& registry = SendableRegistry::GetInstance();
-  registry.AddChild(this, m_leftMotor);
-  registry.AddChild(this, m_rightMotor);
+  wpi::SendableRegistry::AddChild(this, m_leftMotor);
+  wpi::SendableRegistry::AddChild(this, m_rightMotor);
   static int instances = 0;
   ++instances;
-  registry.AddLW(this, "DifferentialDrive", instances);
+  wpi::SendableRegistry::AddLW(this, "DifferentialDrive", instances);
 }
 
 void DifferentialDrive::ArcadeDrive(double xSpeed, double zRotation,
@@ -38,54 +43,19 @@
     reported = true;
   }
 
-  xSpeed = std::clamp(xSpeed, -1.0, 1.0);
   xSpeed = ApplyDeadband(xSpeed, m_deadband);
-
-  zRotation = std::clamp(zRotation, -1.0, 1.0);
   zRotation = ApplyDeadband(zRotation, m_deadband);
 
-  // Square the inputs (while preserving the sign) to increase fine control
-  // while permitting full power.
-  if (squareInputs) {
-    xSpeed = std::copysign(xSpeed * xSpeed, xSpeed);
-    zRotation = std::copysign(zRotation * zRotation, zRotation);
-  }
+  auto [left, right] = ArcadeDriveIK(xSpeed, zRotation, squareInputs);
 
-  double leftMotorOutput;
-  double rightMotorOutput;
-
-  double maxInput =
-      std::copysign(std::max(std::abs(xSpeed), std::abs(zRotation)), xSpeed);
-
-  if (xSpeed >= 0.0) {
-    // First quadrant, else second quadrant
-    if (zRotation >= 0.0) {
-      leftMotorOutput = maxInput;
-      rightMotorOutput = xSpeed - zRotation;
-    } else {
-      leftMotorOutput = xSpeed + zRotation;
-      rightMotorOutput = maxInput;
-    }
-  } else {
-    // Third quadrant, else fourth quadrant
-    if (zRotation >= 0.0) {
-      leftMotorOutput = xSpeed + zRotation;
-      rightMotorOutput = maxInput;
-    } else {
-      leftMotorOutput = maxInput;
-      rightMotorOutput = xSpeed - zRotation;
-    }
-  }
-
-  m_leftMotor->Set(std::clamp(leftMotorOutput, -1.0, 1.0) * m_maxOutput);
-  double maxOutput = m_maxOutput * m_rightSideInvertMultiplier;
-  m_rightMotor->Set(std::clamp(rightMotorOutput, -1.0, 1.0) * maxOutput);
+  m_leftMotor->Set(left);
+  m_rightMotor->Set(right);
 
   Feed();
 }
 
 void DifferentialDrive::CurvatureDrive(double xSpeed, double zRotation,
-                                       bool isQuickTurn) {
+                                       bool allowTurnInPlace) {
   static bool reported = false;
   if (!reported) {
     HAL_Report(HALUsageReporting::kResourceType_RobotDrive,
@@ -93,67 +63,13 @@
     reported = true;
   }
 
-  xSpeed = std::clamp(xSpeed, -1.0, 1.0);
   xSpeed = ApplyDeadband(xSpeed, m_deadband);
-
-  zRotation = std::clamp(zRotation, -1.0, 1.0);
   zRotation = ApplyDeadband(zRotation, m_deadband);
 
-  double angularPower;
-  bool overPower;
+  auto [left, right] = CurvatureDriveIK(xSpeed, zRotation, allowTurnInPlace);
 
-  if (isQuickTurn) {
-    if (std::abs(xSpeed) < m_quickStopThreshold) {
-      m_quickStopAccumulator =
-          (1 - m_quickStopAlpha) * m_quickStopAccumulator +
-          m_quickStopAlpha * std::clamp(zRotation, -1.0, 1.0) * 2;
-    }
-    overPower = true;
-    angularPower = zRotation;
-  } else {
-    overPower = false;
-    angularPower = std::abs(xSpeed) * zRotation - m_quickStopAccumulator;
-
-    if (m_quickStopAccumulator > 1) {
-      m_quickStopAccumulator -= 1;
-    } else if (m_quickStopAccumulator < -1) {
-      m_quickStopAccumulator += 1;
-    } else {
-      m_quickStopAccumulator = 0.0;
-    }
-  }
-
-  double leftMotorOutput = xSpeed + angularPower;
-  double rightMotorOutput = xSpeed - angularPower;
-
-  // If rotation is overpowered, reduce both outputs to within acceptable range
-  if (overPower) {
-    if (leftMotorOutput > 1.0) {
-      rightMotorOutput -= leftMotorOutput - 1.0;
-      leftMotorOutput = 1.0;
-    } else if (rightMotorOutput > 1.0) {
-      leftMotorOutput -= rightMotorOutput - 1.0;
-      rightMotorOutput = 1.0;
-    } else if (leftMotorOutput < -1.0) {
-      rightMotorOutput -= leftMotorOutput + 1.0;
-      leftMotorOutput = -1.0;
-    } else if (rightMotorOutput < -1.0) {
-      leftMotorOutput -= rightMotorOutput + 1.0;
-      rightMotorOutput = -1.0;
-    }
-  }
-
-  // Normalize the wheel speeds
-  double maxMagnitude =
-      std::max(std::abs(leftMotorOutput), std::abs(rightMotorOutput));
-  if (maxMagnitude > 1.0) {
-    leftMotorOutput /= maxMagnitude;
-    rightMotorOutput /= maxMagnitude;
-  }
-
-  m_leftMotor->Set(leftMotorOutput * m_maxOutput);
-  m_rightMotor->Set(rightMotorOutput * m_maxOutput *
-                    m_rightSideInvertMultiplier);
+  m_leftMotor->Set(left * m_maxOutput);
+  m_rightMotor->Set(right * m_maxOutput);
 
   Feed();
 }
@@ -167,12 +83,96 @@
     reported = true;
   }
 
-  leftSpeed = std::clamp(leftSpeed, -1.0, 1.0);
   leftSpeed = ApplyDeadband(leftSpeed, m_deadband);
-
-  rightSpeed = std::clamp(rightSpeed, -1.0, 1.0);
   rightSpeed = ApplyDeadband(rightSpeed, m_deadband);
 
+  auto [left, right] = TankDriveIK(leftSpeed, rightSpeed, squareInputs);
+
+  m_leftMotor->Set(left * m_maxOutput);
+  m_rightMotor->Set(right * m_maxOutput);
+
+  Feed();
+}
+
+DifferentialDrive::WheelSpeeds DifferentialDrive::ArcadeDriveIK(
+    double xSpeed, double zRotation, bool squareInputs) {
+  xSpeed = std::clamp(xSpeed, -1.0, 1.0);
+  zRotation = std::clamp(zRotation, -1.0, 1.0);
+
+  // Square the inputs (while preserving the sign) to increase fine control
+  // while permitting full power.
+  if (squareInputs) {
+    xSpeed = std::copysign(xSpeed * xSpeed, xSpeed);
+    zRotation = std::copysign(zRotation * zRotation, zRotation);
+  }
+
+  double leftSpeed;
+  double rightSpeed;
+
+  double maxInput =
+      std::copysign(std::max(std::abs(xSpeed), std::abs(zRotation)), xSpeed);
+
+  if (xSpeed >= 0.0) {
+    // First quadrant, else second quadrant
+    if (zRotation >= 0.0) {
+      leftSpeed = maxInput;
+      rightSpeed = xSpeed - zRotation;
+    } else {
+      leftSpeed = xSpeed + zRotation;
+      rightSpeed = maxInput;
+    }
+  } else {
+    // Third quadrant, else fourth quadrant
+    if (zRotation >= 0.0) {
+      leftSpeed = xSpeed + zRotation;
+      rightSpeed = maxInput;
+    } else {
+      leftSpeed = maxInput;
+      rightSpeed = xSpeed - zRotation;
+    }
+  }
+
+  // Normalize the wheel speeds
+  double maxMagnitude = std::max(std::abs(leftSpeed), std::abs(rightSpeed));
+  if (maxMagnitude > 1.0) {
+    leftSpeed /= maxMagnitude;
+    rightSpeed /= maxMagnitude;
+  }
+
+  return {leftSpeed, rightSpeed};
+}
+
+DifferentialDrive::WheelSpeeds DifferentialDrive::CurvatureDriveIK(
+    double xSpeed, double zRotation, bool allowTurnInPlace) {
+  xSpeed = std::clamp(xSpeed, -1.0, 1.0);
+  zRotation = std::clamp(zRotation, -1.0, 1.0);
+
+  double leftSpeed = 0.0;
+  double rightSpeed = 0.0;
+
+  if (allowTurnInPlace) {
+    leftSpeed = xSpeed + zRotation;
+    rightSpeed = xSpeed - zRotation;
+  } else {
+    leftSpeed = xSpeed + std::abs(xSpeed) * zRotation;
+    rightSpeed = xSpeed - std::abs(xSpeed) * zRotation;
+  }
+
+  // Normalize wheel speeds
+  double maxMagnitude = std::max(std::abs(leftSpeed), std::abs(rightSpeed));
+  if (maxMagnitude > 1.0) {
+    leftSpeed /= maxMagnitude;
+    rightSpeed /= maxMagnitude;
+  }
+
+  return {leftSpeed, rightSpeed};
+}
+
+DifferentialDrive::WheelSpeeds DifferentialDrive::TankDriveIK(
+    double leftSpeed, double rightSpeed, bool squareInputs) {
+  leftSpeed = std::clamp(leftSpeed, -1.0, 1.0);
+  rightSpeed = std::clamp(rightSpeed, -1.0, 1.0);
+
   // Square the inputs (while preserving the sign) to increase fine control
   // while permitting full power.
   if (squareInputs) {
@@ -180,26 +180,7 @@
     rightSpeed = std::copysign(rightSpeed * rightSpeed, rightSpeed);
   }
 
-  m_leftMotor->Set(leftSpeed * m_maxOutput);
-  m_rightMotor->Set(rightSpeed * m_maxOutput * m_rightSideInvertMultiplier);
-
-  Feed();
-}
-
-void DifferentialDrive::SetQuickStopThreshold(double threshold) {
-  m_quickStopThreshold = threshold;
-}
-
-void DifferentialDrive::SetQuickStopAlpha(double alpha) {
-  m_quickStopAlpha = alpha;
-}
-
-bool DifferentialDrive::IsRightSideInverted() const {
-  return m_rightSideInvertMultiplier == -1.0;
-}
-
-void DifferentialDrive::SetRightSideInverted(bool rightSideInverted) {
-  m_rightSideInvertMultiplier = rightSideInverted ? -1.0 : 1.0;
+  return {leftSpeed, rightSpeed};
 }
 
 void DifferentialDrive::StopMotor() {
@@ -208,21 +189,18 @@
   Feed();
 }
 
-void DifferentialDrive::GetDescription(wpi::raw_ostream& desc) const {
-  desc << "DifferentialDrive";
+std::string DifferentialDrive::GetDescription() const {
+  return "DifferentialDrive";
 }
 
-void DifferentialDrive::InitSendable(SendableBuilder& builder) {
+void DifferentialDrive::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("DifferentialDrive");
   builder.SetActuator(true);
   builder.SetSafeState([=] { StopMotor(); });
   builder.AddDoubleProperty(
-      "Left Motor Speed", [=]() { return m_leftMotor->Get(); },
+      "Left Motor Speed", [=] { return m_leftMotor->Get(); },
       [=](double value) { m_leftMotor->Set(value); });
   builder.AddDoubleProperty(
-      "Right Motor Speed",
-      [=]() { return m_rightMotor->Get() * m_rightSideInvertMultiplier; },
-      [=](double value) {
-        m_rightMotor->Set(value * m_rightSideInvertMultiplier);
-      });
+      "Right Motor Speed", [=] { return m_rightMotor->Get(); },
+      [=](double value) { m_rightMotor->Set(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/drive/KilloughDrive.cpp b/wpilibc/src/main/native/cpp/drive/KilloughDrive.cpp
index 983ce6a..c847678 100644
--- a/wpilibc/src/main/native/cpp/drive/KilloughDrive.cpp
+++ b/wpilibc/src/main/native/cpp/drive/KilloughDrive.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/drive/KilloughDrive.h"
 
@@ -11,14 +8,23 @@
 #include <cmath>
 
 #include <hal/FRCUsageReporting.h>
-#include <wpi/math>
+#include <wpi/numbers>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/MathUtil.h"
 #include "frc/SpeedController.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
+#if defined(_MSC_VER)
+#pragma warning(disable : 4996)  // was declared deprecated
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 KilloughDrive::KilloughDrive(SpeedController& leftMotor,
                              SpeedController& rightMotor,
                              SpeedController& backMotor)
@@ -32,19 +38,18 @@
     : m_leftMotor(&leftMotor),
       m_rightMotor(&rightMotor),
       m_backMotor(&backMotor) {
-  m_leftVec = {std::cos(leftMotorAngle * (wpi::math::pi / 180.0)),
-               std::sin(leftMotorAngle * (wpi::math::pi / 180.0))};
-  m_rightVec = {std::cos(rightMotorAngle * (wpi::math::pi / 180.0)),
-                std::sin(rightMotorAngle * (wpi::math::pi / 180.0))};
-  m_backVec = {std::cos(backMotorAngle * (wpi::math::pi / 180.0)),
-               std::sin(backMotorAngle * (wpi::math::pi / 180.0))};
-  auto& registry = SendableRegistry::GetInstance();
-  registry.AddChild(this, m_leftMotor);
-  registry.AddChild(this, m_rightMotor);
-  registry.AddChild(this, m_backMotor);
+  m_leftVec = {std::cos(leftMotorAngle * (wpi::numbers::pi / 180.0)),
+               std::sin(leftMotorAngle * (wpi::numbers::pi / 180.0))};
+  m_rightVec = {std::cos(rightMotorAngle * (wpi::numbers::pi / 180.0)),
+                std::sin(rightMotorAngle * (wpi::numbers::pi / 180.0))};
+  m_backVec = {std::cos(backMotorAngle * (wpi::numbers::pi / 180.0)),
+               std::sin(backMotorAngle * (wpi::numbers::pi / 180.0))};
+  wpi::SendableRegistry::AddChild(this, m_leftMotor);
+  wpi::SendableRegistry::AddChild(this, m_rightMotor);
+  wpi::SendableRegistry::AddChild(this, m_backMotor);
   static int instances = 0;
   ++instances;
-  registry.AddLW(this, "KilloughDrive", instances);
+  wpi::SendableRegistry::AddLW(this, "KilloughDrive", instances);
 }
 
 void KilloughDrive::DriveCartesian(double ySpeed, double xSpeed,
@@ -55,26 +60,15 @@
     reported = true;
   }
 
-  ySpeed = std::clamp(ySpeed, -1.0, 1.0);
   ySpeed = ApplyDeadband(ySpeed, m_deadband);
-
-  xSpeed = std::clamp(xSpeed, -1.0, 1.0);
   xSpeed = ApplyDeadband(xSpeed, m_deadband);
 
-  // Compensate for gyro angle.
-  Vector2d input{ySpeed, xSpeed};
-  input.Rotate(-gyroAngle);
+  auto [left, right, back] =
+      DriveCartesianIK(ySpeed, xSpeed, zRotation, gyroAngle);
 
-  double wheelSpeeds[3];
-  wheelSpeeds[kLeft] = input.ScalarProject(m_leftVec) + zRotation;
-  wheelSpeeds[kRight] = input.ScalarProject(m_rightVec) + zRotation;
-  wheelSpeeds[kBack] = input.ScalarProject(m_backVec) + zRotation;
-
-  Normalize(wheelSpeeds);
-
-  m_leftMotor->Set(wheelSpeeds[kLeft] * m_maxOutput);
-  m_rightMotor->Set(wheelSpeeds[kRight] * m_maxOutput);
-  m_backMotor->Set(wheelSpeeds[kBack] * m_maxOutput);
+  m_leftMotor->Set(left * m_maxOutput);
+  m_rightMotor->Set(right * m_maxOutput);
+  m_backMotor->Set(back * m_maxOutput);
 
   Feed();
 }
@@ -87,11 +81,32 @@
     reported = true;
   }
 
-  DriveCartesian(magnitude * std::sin(angle * (wpi::math::pi / 180.0)),
-                 magnitude * std::cos(angle * (wpi::math::pi / 180.0)),
+  DriveCartesian(magnitude * std::sin(angle * (wpi::numbers::pi / 180.0)),
+                 magnitude * std::cos(angle * (wpi::numbers::pi / 180.0)),
                  zRotation, 0.0);
 }
 
+KilloughDrive::WheelSpeeds KilloughDrive::DriveCartesianIK(double ySpeed,
+                                                           double xSpeed,
+                                                           double zRotation,
+                                                           double gyroAngle) {
+  ySpeed = std::clamp(ySpeed, -1.0, 1.0);
+  xSpeed = std::clamp(xSpeed, -1.0, 1.0);
+
+  // Compensate for gyro angle.
+  Vector2d input{ySpeed, xSpeed};
+  input.Rotate(-gyroAngle);
+
+  double wheelSpeeds[3];
+  wheelSpeeds[kLeft] = input.ScalarProject(m_leftVec) + zRotation;
+  wheelSpeeds[kRight] = input.ScalarProject(m_rightVec) + zRotation;
+  wheelSpeeds[kBack] = input.ScalarProject(m_backVec) + zRotation;
+
+  Normalize(wheelSpeeds);
+
+  return {wheelSpeeds[kLeft], wheelSpeeds[kRight], wheelSpeeds[kBack]};
+}
+
 void KilloughDrive::StopMotor() {
   m_leftMotor->StopMotor();
   m_rightMotor->StopMotor();
@@ -99,21 +114,21 @@
   Feed();
 }
 
-void KilloughDrive::GetDescription(wpi::raw_ostream& desc) const {
-  desc << "KilloughDrive";
+std::string KilloughDrive::GetDescription() const {
+  return "KilloughDrive";
 }
 
-void KilloughDrive::InitSendable(SendableBuilder& builder) {
+void KilloughDrive::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("KilloughDrive");
   builder.SetActuator(true);
   builder.SetSafeState([=] { StopMotor(); });
   builder.AddDoubleProperty(
-      "Left Motor Speed", [=]() { return m_leftMotor->Get(); },
+      "Left Motor Speed", [=] { return m_leftMotor->Get(); },
       [=](double value) { m_leftMotor->Set(value); });
   builder.AddDoubleProperty(
-      "Right Motor Speed", [=]() { return m_rightMotor->Get(); },
+      "Right Motor Speed", [=] { return m_rightMotor->Get(); },
       [=](double value) { m_rightMotor->Set(value); });
   builder.AddDoubleProperty(
-      "Back Motor Speed", [=]() { return m_backMotor->Get(); },
+      "Back Motor Speed", [=] { return m_backMotor->Get(); },
       [=](double value) { m_backMotor->Set(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp b/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp
index cb11d8a..b74c611 100644
--- a/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp
+++ b/wpilibc/src/main/native/cpp/drive/MecanumDrive.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/drive/MecanumDrive.h"
 
@@ -11,15 +8,24 @@
 #include <cmath>
 
 #include <hal/FRCUsageReporting.h>
-#include <wpi/math>
+#include <wpi/numbers>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/MathUtil.h"
 #include "frc/SpeedController.h"
 #include "frc/drive/Vector2d.h"
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
+#if defined(_MSC_VER)
+#pragma warning(disable : 4996)  // was declared deprecated
+#elif defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
 MecanumDrive::MecanumDrive(SpeedController& frontLeftMotor,
                            SpeedController& rearLeftMotor,
                            SpeedController& frontRightMotor,
@@ -28,14 +34,13 @@
       m_rearLeftMotor(&rearLeftMotor),
       m_frontRightMotor(&frontRightMotor),
       m_rearRightMotor(&rearRightMotor) {
-  auto& registry = SendableRegistry::GetInstance();
-  registry.AddChild(this, m_frontLeftMotor);
-  registry.AddChild(this, m_rearLeftMotor);
-  registry.AddChild(this, m_frontRightMotor);
-  registry.AddChild(this, m_rearRightMotor);
+  wpi::SendableRegistry::AddChild(this, m_frontLeftMotor);
+  wpi::SendableRegistry::AddChild(this, m_rearLeftMotor);
+  wpi::SendableRegistry::AddChild(this, m_frontRightMotor);
+  wpi::SendableRegistry::AddChild(this, m_rearRightMotor);
   static int instances = 0;
   ++instances;
-  registry.AddLW(this, "MecanumDrive", instances);
+  wpi::SendableRegistry::AddLW(this, "MecanumDrive", instances);
 }
 
 void MecanumDrive::DriveCartesian(double ySpeed, double xSpeed,
@@ -46,30 +51,16 @@
     reported = true;
   }
 
-  ySpeed = std::clamp(ySpeed, -1.0, 1.0);
   ySpeed = ApplyDeadband(ySpeed, m_deadband);
-
-  xSpeed = std::clamp(xSpeed, -1.0, 1.0);
   xSpeed = ApplyDeadband(xSpeed, m_deadband);
 
-  // Compensate for gyro angle.
-  Vector2d input{ySpeed, xSpeed};
-  input.Rotate(-gyroAngle);
+  auto [frontLeft, frontRight, rearLeft, rearRight] =
+      DriveCartesianIK(ySpeed, xSpeed, zRotation, gyroAngle);
 
-  double wheelSpeeds[4];
-  wheelSpeeds[kFrontLeft] = input.x + input.y + zRotation;
-  wheelSpeeds[kFrontRight] = -input.x + input.y - zRotation;
-  wheelSpeeds[kRearLeft] = -input.x + input.y + zRotation;
-  wheelSpeeds[kRearRight] = input.x + input.y - zRotation;
-
-  Normalize(wheelSpeeds);
-
-  m_frontLeftMotor->Set(wheelSpeeds[kFrontLeft] * m_maxOutput);
-  m_frontRightMotor->Set(wheelSpeeds[kFrontRight] * m_maxOutput *
-                         m_rightSideInvertMultiplier);
-  m_rearLeftMotor->Set(wheelSpeeds[kRearLeft] * m_maxOutput);
-  m_rearRightMotor->Set(wheelSpeeds[kRearRight] * m_maxOutput *
-                        m_rightSideInvertMultiplier);
+  m_frontLeftMotor->Set(frontLeft * m_maxOutput);
+  m_frontRightMotor->Set(frontRight * m_maxOutput);
+  m_rearLeftMotor->Set(rearLeft * m_maxOutput);
+  m_rearRightMotor->Set(rearRight * m_maxOutput);
 
   Feed();
 }
@@ -82,19 +73,11 @@
     reported = true;
   }
 
-  DriveCartesian(magnitude * std::sin(angle * (wpi::math::pi / 180.0)),
-                 magnitude * std::cos(angle * (wpi::math::pi / 180.0)),
+  DriveCartesian(magnitude * std::cos(angle * (wpi::numbers::pi / 180.0)),
+                 magnitude * std::sin(angle * (wpi::numbers::pi / 180.0)),
                  zRotation, 0.0);
 }
 
-bool MecanumDrive::IsRightSideInverted() const {
-  return m_rightSideInvertMultiplier == -1.0;
-}
-
-void MecanumDrive::SetRightSideInverted(bool rightSideInverted) {
-  m_rightSideInvertMultiplier = rightSideInverted ? -1.0 : 1.0;
-}
-
 void MecanumDrive::StopMotor() {
   m_frontLeftMotor->StopMotor();
   m_frontRightMotor->StopMotor();
@@ -103,30 +86,47 @@
   Feed();
 }
 
-void MecanumDrive::GetDescription(wpi::raw_ostream& desc) const {
-  desc << "MecanumDrive";
+MecanumDrive::WheelSpeeds MecanumDrive::DriveCartesianIK(double ySpeed,
+                                                         double xSpeed,
+                                                         double zRotation,
+                                                         double gyroAngle) {
+  ySpeed = std::clamp(ySpeed, -1.0, 1.0);
+  xSpeed = std::clamp(xSpeed, -1.0, 1.0);
+
+  // Compensate for gyro angle.
+  Vector2d input{ySpeed, xSpeed};
+  input.Rotate(-gyroAngle);
+
+  double wheelSpeeds[4];
+  wheelSpeeds[kFrontLeft] = input.x + input.y + zRotation;
+  wheelSpeeds[kFrontRight] = input.x - input.y - zRotation;
+  wheelSpeeds[kRearLeft] = input.x - input.y + zRotation;
+  wheelSpeeds[kRearRight] = input.x + input.y - zRotation;
+
+  Normalize(wheelSpeeds);
+
+  return {wheelSpeeds[kFrontLeft], wheelSpeeds[kFrontRight],
+          wheelSpeeds[kRearLeft], wheelSpeeds[kRearRight]};
 }
 
-void MecanumDrive::InitSendable(SendableBuilder& builder) {
+std::string MecanumDrive::GetDescription() const {
+  return "MecanumDrive";
+}
+
+void MecanumDrive::InitSendable(wpi::SendableBuilder& builder) {
   builder.SetSmartDashboardType("MecanumDrive");
   builder.SetActuator(true);
   builder.SetSafeState([=] { StopMotor(); });
   builder.AddDoubleProperty(
-      "Front Left Motor Speed", [=]() { return m_frontLeftMotor->Get(); },
+      "Front Left Motor Speed", [=] { return m_frontLeftMotor->Get(); },
       [=](double value) { m_frontLeftMotor->Set(value); });
   builder.AddDoubleProperty(
-      "Front Right Motor Speed",
-      [=]() { return m_frontRightMotor->Get() * m_rightSideInvertMultiplier; },
-      [=](double value) {
-        m_frontRightMotor->Set(value * m_rightSideInvertMultiplier);
-      });
+      "Front Right Motor Speed", [=] { return m_frontRightMotor->Get(); },
+      [=](double value) { m_frontRightMotor->Set(value); });
   builder.AddDoubleProperty(
-      "Rear Left Motor Speed", [=]() { return m_rearLeftMotor->Get(); },
+      "Rear Left Motor Speed", [=] { return m_rearLeftMotor->Get(); },
       [=](double value) { m_rearLeftMotor->Set(value); });
   builder.AddDoubleProperty(
-      "Rear Right Motor Speed",
-      [=]() { return m_rearRightMotor->Get() * m_rightSideInvertMultiplier; },
-      [=](double value) {
-        m_rearRightMotor->Set(value * m_rightSideInvertMultiplier);
-      });
+      "Rear Right Motor Speed", [=] { return m_rearRightMotor->Get(); },
+      [=](double value) { m_rearRightMotor->Set(value); });
 }
diff --git a/wpilibc/src/main/native/cpp/drive/RobotDriveBase.cpp b/wpilibc/src/main/native/cpp/drive/RobotDriveBase.cpp
index fbce5a1..2159958 100644
--- a/wpilibc/src/main/native/cpp/drive/RobotDriveBase.cpp
+++ b/wpilibc/src/main/native/cpp/drive/RobotDriveBase.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/drive/RobotDriveBase.h"
 
@@ -13,32 +10,32 @@
 
 #include <hal/FRCUsageReporting.h>
 
-#include "frc/Base.h"
-#include "frc/SpeedController.h"
+#include "frc/MathUtil.h"
+#include "frc/motorcontrol/MotorController.h"
 
 using namespace frc;
 
-RobotDriveBase::RobotDriveBase() { SetSafetyEnabled(true); }
-
-void RobotDriveBase::SetDeadband(double deadband) { m_deadband = deadband; }
-
-void RobotDriveBase::SetMaxOutput(double maxOutput) { m_maxOutput = maxOutput; }
-
-void RobotDriveBase::FeedWatchdog() { Feed(); }
-
-double RobotDriveBase::ApplyDeadband(double value, double deadband) {
-  if (std::abs(value) > deadband) {
-    if (value > 0.0) {
-      return (value - deadband) / (1.0 - deadband);
-    } else {
-      return (value + deadband) / (1.0 - deadband);
-    }
-  } else {
-    return 0.0;
-  }
+RobotDriveBase::RobotDriveBase() {
+  SetSafetyEnabled(true);
 }
 
-void RobotDriveBase::Normalize(wpi::MutableArrayRef<double> wheelSpeeds) {
+void RobotDriveBase::SetDeadband(double deadband) {
+  m_deadband = deadband;
+}
+
+void RobotDriveBase::SetMaxOutput(double maxOutput) {
+  m_maxOutput = maxOutput;
+}
+
+void RobotDriveBase::FeedWatchdog() {
+  Feed();
+}
+
+double RobotDriveBase::ApplyDeadband(double value, double deadband) {
+  return frc::ApplyDeadband(value, deadband);
+}
+
+void RobotDriveBase::Normalize(wpi::span<double> wheelSpeeds) {
   double maxMagnitude = std::abs(wheelSpeeds[0]);
   for (size_t i = 1; i < wheelSpeeds.size(); i++) {
     double temp = std::abs(wheelSpeeds[i]);
diff --git a/wpilibc/src/main/native/cpp/drive/Vector2d.cpp b/wpilibc/src/main/native/cpp/drive/Vector2d.cpp
index a6e68a6..a342dc2 100644
--- a/wpilibc/src/main/native/cpp/drive/Vector2d.cpp
+++ b/wpilibc/src/main/native/cpp/drive/Vector2d.cpp
@@ -1,15 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/drive/Vector2d.h"
 
 #include <cmath>
 
-#include <wpi/math>
+#include <wpi/numbers>
 
 using namespace frc;
 
@@ -19,8 +16,8 @@
 }
 
 void Vector2d::Rotate(double angle) {
-  double cosA = std::cos(angle * (wpi::math::pi / 180.0));
-  double sinA = std::sin(angle * (wpi::math::pi / 180.0));
+  double cosA = std::cos(angle * (wpi::numbers::pi / 180.0));
+  double sinA = std::sin(angle * (wpi::numbers::pi / 180.0));
   double out[2];
   out[0] = x * cosA - y * sinA;
   out[1] = x * sinA + y * cosA;
@@ -32,7 +29,9 @@
   return x * vec.x + y * vec.y;
 }
 
-double Vector2d::Magnitude() const { return std::sqrt(x * x + y * y); }
+double Vector2d::Magnitude() const {
+  return std::sqrt(x * x + y * y);
+}
 
 double Vector2d::ScalarProject(const Vector2d& vec) const {
   return Dot(vec) / vec.Magnitude();
diff --git a/wpilibc/src/main/native/cpp/filters/Filter.cpp b/wpilibc/src/main/native/cpp/filters/Filter.cpp
deleted file mode 100644
index c25d7af..0000000
--- a/wpilibc/src/main/native/cpp/filters/Filter.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/filters/Filter.h"
-
-#include "frc/Base.h"
-
-using namespace frc;
-
-Filter::Filter(PIDSource& source)
-    : m_source(std::shared_ptr<PIDSource>(&source, NullDeleter<PIDSource>())) {}
-
-Filter::Filter(std::shared_ptr<PIDSource> source)
-    : m_source(std::move(source)) {}
-
-void Filter::SetPIDSourceType(PIDSourceType pidSource) {
-  m_source->SetPIDSourceType(pidSource);
-}
-
-PIDSourceType Filter::GetPIDSourceType() const {
-  return m_source->GetPIDSourceType();
-}
-
-double Filter::PIDGetSource() { return m_source->PIDGet(); }
diff --git a/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp b/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp
deleted file mode 100644
index 434cc27..0000000
--- a/wpilibc/src/main/native/cpp/filters/LinearDigitalFilter.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/filters/LinearDigitalFilter.h"
-
-#include <cassert>
-#include <cmath>
-
-#include <hal/FRCUsageReporting.h>
-
-using namespace frc;
-
-LinearDigitalFilter::LinearDigitalFilter(PIDSource& source,
-                                         wpi::ArrayRef<double> ffGains,
-                                         wpi::ArrayRef<double> fbGains)
-    : Filter(source),
-      m_inputs(ffGains.size()),
-      m_outputs(fbGains.size()),
-      m_inputGains(ffGains),
-      m_outputGains(fbGains) {
-  static int instances = 0;
-  instances++;
-  HAL_Report(HALUsageReporting::kResourceType_LinearFilter, instances);
-}
-
-LinearDigitalFilter::LinearDigitalFilter(PIDSource& source,
-                                         std::initializer_list<double> ffGains,
-                                         std::initializer_list<double> fbGains)
-    : LinearDigitalFilter(source,
-                          wpi::makeArrayRef(ffGains.begin(), ffGains.end()),
-                          wpi::makeArrayRef(fbGains.begin(), fbGains.end())) {}
-
-LinearDigitalFilter::LinearDigitalFilter(std::shared_ptr<PIDSource> source,
-                                         wpi::ArrayRef<double> ffGains,
-                                         wpi::ArrayRef<double> fbGains)
-    : Filter(source),
-      m_inputs(ffGains.size()),
-      m_outputs(fbGains.size()),
-      m_inputGains(ffGains),
-      m_outputGains(fbGains) {
-  static int instances = 0;
-  instances++;
-  HAL_Report(HALUsageReporting::kResourceType_LinearFilter, instances);
-}
-
-LinearDigitalFilter::LinearDigitalFilter(std::shared_ptr<PIDSource> source,
-                                         std::initializer_list<double> ffGains,
-                                         std::initializer_list<double> fbGains)
-    : LinearDigitalFilter(source,
-                          wpi::makeArrayRef(ffGains.begin(), ffGains.end()),
-                          wpi::makeArrayRef(fbGains.begin(), fbGains.end())) {}
-
-LinearDigitalFilter LinearDigitalFilter::SinglePoleIIR(PIDSource& source,
-                                                       double timeConstant,
-                                                       double period) {
-  double gain = std::exp(-period / timeConstant);
-  return LinearDigitalFilter(source, {1.0 - gain}, {-gain});
-}
-
-LinearDigitalFilter LinearDigitalFilter::HighPass(PIDSource& source,
-                                                  double timeConstant,
-                                                  double period) {
-  double gain = std::exp(-period / timeConstant);
-  return LinearDigitalFilter(source, {gain, -gain}, {-gain});
-}
-
-LinearDigitalFilter LinearDigitalFilter::MovingAverage(PIDSource& source,
-                                                       int taps) {
-  assert(taps > 0);
-
-  std::vector<double> gains(taps, 1.0 / taps);
-  return LinearDigitalFilter(source, gains, {});
-}
-
-LinearDigitalFilter LinearDigitalFilter::SinglePoleIIR(
-    std::shared_ptr<PIDSource> source, double timeConstant, double period) {
-  double gain = std::exp(-period / timeConstant);
-  return LinearDigitalFilter(std::move(source), {1.0 - gain}, {-gain});
-}
-
-LinearDigitalFilter LinearDigitalFilter::HighPass(
-    std::shared_ptr<PIDSource> source, double timeConstant, double period) {
-  double gain = std::exp(-period / timeConstant);
-  return LinearDigitalFilter(std::move(source), {gain, -gain}, {-gain});
-}
-
-LinearDigitalFilter LinearDigitalFilter::MovingAverage(
-    std::shared_ptr<PIDSource> source, int taps) {
-  assert(taps > 0);
-
-  std::vector<double> gains(taps, 1.0 / taps);
-  return LinearDigitalFilter(std::move(source), gains, {});
-}
-
-double LinearDigitalFilter::Get() const {
-  double retVal = 0.0;
-
-  // Calculate the new value
-  for (size_t i = 0; i < m_inputGains.size(); i++) {
-    retVal += m_inputs[i] * m_inputGains[i];
-  }
-  for (size_t i = 0; i < m_outputGains.size(); i++) {
-    retVal -= m_outputs[i] * m_outputGains[i];
-  }
-
-  return retVal;
-}
-
-void LinearDigitalFilter::Reset() {
-  m_inputs.reset();
-  m_outputs.reset();
-}
-
-double LinearDigitalFilter::PIDGet() {
-  double retVal = 0.0;
-
-  // Rotate the inputs
-  m_inputs.push_front(PIDGetSource());
-
-  // Calculate the new value
-  for (size_t i = 0; i < m_inputGains.size(); i++) {
-    retVal += m_inputs[i] * m_inputGains[i];
-  }
-  for (size_t i = 0; i < m_outputGains.size(); i++) {
-    retVal -= m_outputs[i] * m_outputGains[i];
-  }
-
-  // Rotate the outputs
-  m_outputs.push_front(retVal);
-
-  return retVal;
-}
diff --git a/wpilibc/src/main/native/cpp/frc2/Timer.cpp b/wpilibc/src/main/native/cpp/frc2/Timer.cpp
deleted file mode 100644
index 36da4c6..0000000
--- a/wpilibc/src/main/native/cpp/frc2/Timer.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2008-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc2/Timer.h"
-
-#include <chrono>
-#include <thread>
-
-#include "frc/DriverStation.h"
-#include "frc/RobotController.h"
-
-namespace frc2 {
-
-void Wait(units::second_t seconds) {
-  std::this_thread::sleep_for(
-      std::chrono::duration<double>(seconds.to<double>()));
-}
-
-units::second_t GetTime() {
-  using std::chrono::duration;
-  using std::chrono::duration_cast;
-  using std::chrono::system_clock;
-
-  return units::second_t(
-      duration_cast<duration<double>>(system_clock::now().time_since_epoch())
-          .count());
-}
-
-}  // namespace frc2
-
-using namespace frc2;
-
-Timer::Timer() { Reset(); }
-
-Timer::Timer(const Timer& rhs)
-    : m_startTime(rhs.m_startTime),
-      m_accumulatedTime(rhs.m_accumulatedTime),
-      m_running(rhs.m_running) {}
-
-Timer& Timer::operator=(const Timer& rhs) {
-  std::scoped_lock lock(m_mutex, rhs.m_mutex);
-
-  m_startTime = rhs.m_startTime;
-  m_accumulatedTime = rhs.m_accumulatedTime;
-  m_running = rhs.m_running;
-
-  return *this;
-}
-
-Timer::Timer(Timer&& rhs)
-    : m_startTime(std::move(rhs.m_startTime)),
-      m_accumulatedTime(std::move(rhs.m_accumulatedTime)),
-      m_running(std::move(rhs.m_running)) {}
-
-Timer& Timer::operator=(Timer&& rhs) {
-  std::scoped_lock lock(m_mutex, rhs.m_mutex);
-
-  m_startTime = std::move(rhs.m_startTime);
-  m_accumulatedTime = std::move(rhs.m_accumulatedTime);
-  m_running = std::move(rhs.m_running);
-
-  return *this;
-}
-
-units::second_t Timer::Get() const {
-  units::second_t result;
-  units::second_t currentTime = GetFPGATimestamp();
-
-  std::scoped_lock lock(m_mutex);
-  if (m_running) {
-    result = (currentTime - m_startTime) + m_accumulatedTime;
-  } else {
-    result = m_accumulatedTime;
-  }
-
-  return result;
-}
-
-void Timer::Reset() {
-  std::scoped_lock lock(m_mutex);
-  m_accumulatedTime = 0_s;
-  m_startTime = GetFPGATimestamp();
-}
-
-void Timer::Start() {
-  std::scoped_lock lock(m_mutex);
-  if (!m_running) {
-    m_startTime = GetFPGATimestamp();
-    m_running = true;
-  }
-}
-
-void Timer::Stop() {
-  units::second_t temp = Get();
-
-  std::scoped_lock lock(m_mutex);
-  if (m_running) {
-    m_accumulatedTime = temp;
-    m_running = false;
-  }
-}
-
-bool Timer::HasElapsed(units::second_t period) const { return Get() > period; }
-
-bool Timer::HasPeriodPassed(units::second_t period) {
-  return AdvanceIfElapsed(period);
-}
-
-bool Timer::AdvanceIfElapsed(units::second_t period) {
-  if (Get() > period) {
-    std::scoped_lock lock(m_mutex);
-    // Advance the start time by the period.
-    m_startTime += period;
-    // Don't set it to the current time... we want to avoid drift.
-    return true;
-  } else {
-    return false;
-  }
-}
-
-units::second_t Timer::GetFPGATimestamp() {
-  // FPGA returns the timestamp in microseconds
-  return units::second_t(frc::RobotController::GetFPGATime() * 1.0e-6);
-}
-
-units::second_t Timer::GetMatchTime() {
-  return units::second_t(frc::DriverStation::GetInstance().GetMatchTime());
-}
diff --git a/wpilibc/src/main/native/cpp/interfaces/Potentiometer.cpp b/wpilibc/src/main/native/cpp/interfaces/Potentiometer.cpp
deleted file mode 100644
index 44f4aab..0000000
--- a/wpilibc/src/main/native/cpp/interfaces/Potentiometer.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2015-2018 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/interfaces/Potentiometer.h"
-
-#include "frc/Utility.h"
-
-using namespace frc;
-
-void Potentiometer::SetPIDSourceType(PIDSourceType pidSource) {
-  if (wpi_assert(pidSource == PIDSourceType::kDisplacement)) {
-    m_pidSource = pidSource;
-  }
-}
diff --git a/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp b/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp
index 517ecf7..f8be7de 100644
--- a/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp
+++ b/wpilibc/src/main/native/cpp/livewindow/LiveWindow.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2012-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/livewindow/LiveWindow.h"
 
@@ -11,153 +8,191 @@
 #include <networktables/NetworkTableEntry.h>
 #include <networktables/NetworkTableInstance.h>
 #include <wpi/mutex.h>
+#include <wpi/sendable/Sendable.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-#include "frc/smartdashboard/Sendable.h"
 #include "frc/smartdashboard/SendableBuilderImpl.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
-using wpi::Twine;
+namespace {
+struct Component {
+  bool firstTime = true;
+  bool telemetryEnabled = true;
+};
 
-struct LiveWindow::Impl {
-  Impl();
-
-  struct Component {
-    bool firstTime = true;
-    bool telemetryEnabled = true;
-  };
+struct Instance {
+  Instance() {
+    wpi::SendableRegistry::SetLiveWindowBuilderFactory(
+        [] { return std::make_unique<SendableBuilderImpl>(); });
+  }
 
   wpi::mutex mutex;
 
-  SendableRegistry& registry;
-  int dataHandle;
+  int dataHandle = wpi::SendableRegistry::GetDataHandle();
 
-  std::shared_ptr<nt::NetworkTable> liveWindowTable;
-  std::shared_ptr<nt::NetworkTable> statusTable;
-  nt::NetworkTableEntry enabledEntry;
+  std::shared_ptr<nt::NetworkTable> liveWindowTable =
+      nt::NetworkTableInstance::GetDefault().GetTable("LiveWindow");
+  std::shared_ptr<nt::NetworkTable> statusTable =
+      liveWindowTable->GetSubTable(".status");
+  nt::NetworkTableEntry enabledEntry = statusTable->GetEntry("LW Enabled");
 
   bool startLiveWindow = false;
   bool liveWindowEnabled = false;
   bool telemetryEnabled = true;
 
-  std::shared_ptr<Component> GetOrAdd(Sendable* sendable);
-};
+  std::function<void()> enabled;
+  std::function<void()> disabled;
 
-LiveWindow::Impl::Impl()
-    : registry(SendableRegistry::GetInstance()),
-      dataHandle(registry.GetDataHandle()),
-      liveWindowTable(
-          nt::NetworkTableInstance::GetDefault().GetTable("LiveWindow")) {
-  statusTable = liveWindowTable->GetSubTable(".status");
-  enabledEntry = statusTable->GetEntry("LW Enabled");
+  std::shared_ptr<Component> GetOrAdd(wpi::Sendable* sendable);
+};
+}  // namespace
+
+static Instance& GetInstance() {
+  static Instance instance;
+  return instance;
 }
 
-std::shared_ptr<LiveWindow::Impl::Component> LiveWindow::Impl::GetOrAdd(
-    Sendable* sendable) {
+std::shared_ptr<Component> Instance::GetOrAdd(wpi::Sendable* sendable) {
   auto data = std::static_pointer_cast<Component>(
-      registry.GetData(sendable, dataHandle));
+      wpi::SendableRegistry::GetData(sendable, dataHandle));
   if (!data) {
     data = std::make_shared<Component>();
-    registry.SetData(sendable, dataHandle, data);
+    wpi::SendableRegistry::SetData(sendable, dataHandle, data);
   }
   return data;
 }
 
 LiveWindow* LiveWindow::GetInstance() {
+  ::GetInstance();
   static LiveWindow instance;
   return &instance;
 }
 
-void LiveWindow::EnableTelemetry(Sendable* sendable) {
-  std::scoped_lock lock(m_impl->mutex);
-  // Re-enable global setting in case DisableAllTelemetry() was called.
-  m_impl->telemetryEnabled = true;
-  m_impl->GetOrAdd(sendable)->telemetryEnabled = true;
+void LiveWindow::SetEnabledCallback(std::function<void()> func) {
+  ::GetInstance().enabled = func;
 }
 
-void LiveWindow::DisableTelemetry(Sendable* sendable) {
-  std::scoped_lock lock(m_impl->mutex);
-  m_impl->GetOrAdd(sendable)->telemetryEnabled = false;
+void LiveWindow::SetDisabledCallback(std::function<void()> func) {
+  ::GetInstance().disabled = func;
+}
+
+void LiveWindow::EnableTelemetry(wpi::Sendable* sendable) {
+  auto& inst = ::GetInstance();
+  std::scoped_lock lock(inst.mutex);
+  // Re-enable global setting in case DisableAllTelemetry() was called.
+  inst.telemetryEnabled = true;
+  inst.GetOrAdd(sendable)->telemetryEnabled = true;
+}
+
+void LiveWindow::DisableTelemetry(wpi::Sendable* sendable) {
+  auto& inst = ::GetInstance();
+  std::scoped_lock lock(inst.mutex);
+  inst.GetOrAdd(sendable)->telemetryEnabled = false;
 }
 
 void LiveWindow::DisableAllTelemetry() {
-  std::scoped_lock lock(m_impl->mutex);
-  m_impl->telemetryEnabled = false;
-  m_impl->registry.ForeachLiveWindow(m_impl->dataHandle, [&](auto& cbdata) {
-    if (!cbdata.data) cbdata.data = std::make_shared<Impl::Component>();
-    std::static_pointer_cast<Impl::Component>(cbdata.data)->telemetryEnabled =
-        false;
+  auto& inst = ::GetInstance();
+  std::scoped_lock lock(inst.mutex);
+  inst.telemetryEnabled = false;
+  wpi::SendableRegistry::ForeachLiveWindow(inst.dataHandle, [&](auto& cbdata) {
+    if (!cbdata.data) {
+      cbdata.data = std::make_shared<Component>();
+    }
+    std::static_pointer_cast<Component>(cbdata.data)->telemetryEnabled = false;
   });
 }
 
-bool LiveWindow::IsEnabled() const {
-  std::scoped_lock lock(m_impl->mutex);
-  return m_impl->liveWindowEnabled;
+bool LiveWindow::IsEnabled() {
+  auto& inst = ::GetInstance();
+  std::scoped_lock lock(inst.mutex);
+  return inst.liveWindowEnabled;
 }
 
 void LiveWindow::SetEnabled(bool enabled) {
-  std::scoped_lock lock(m_impl->mutex);
-  if (m_impl->liveWindowEnabled == enabled) return;
-  m_impl->startLiveWindow = enabled;
-  m_impl->liveWindowEnabled = enabled;
+  auto& inst = ::GetInstance();
+  std::scoped_lock lock(inst.mutex);
+  if (inst.liveWindowEnabled == enabled) {
+    return;
+  }
+  inst.startLiveWindow = enabled;
+  inst.liveWindowEnabled = enabled;
   // Force table generation now to make sure everything is defined
   UpdateValuesUnsafe();
   if (enabled) {
-    if (this->enabled) this->enabled();
+    if (inst.enabled) {
+      inst.enabled();
+    }
   } else {
-    m_impl->registry.ForeachLiveWindow(m_impl->dataHandle, [&](auto& cbdata) {
-      cbdata.builder.StopLiveWindowMode();
-    });
-    if (this->disabled) this->disabled();
+    wpi::SendableRegistry::ForeachLiveWindow(
+        inst.dataHandle, [&](auto& cbdata) {
+          static_cast<SendableBuilderImpl&>(cbdata.builder)
+              .StopLiveWindowMode();
+        });
+    if (inst.disabled) {
+      inst.disabled();
+    }
   }
-  m_impl->enabledEntry.SetBoolean(enabled);
+  inst.enabledEntry.SetBoolean(enabled);
 }
 
 void LiveWindow::UpdateValues() {
-  std::scoped_lock lock(m_impl->mutex);
+  auto& inst = ::GetInstance();
+  std::scoped_lock lock(inst.mutex);
   UpdateValuesUnsafe();
 }
 
 void LiveWindow::UpdateValuesUnsafe() {
+  auto& inst = ::GetInstance();
   // Only do this if either LiveWindow mode or telemetry is enabled.
-  if (!m_impl->liveWindowEnabled && !m_impl->telemetryEnabled) return;
+  if (!inst.liveWindowEnabled && !inst.telemetryEnabled) {
+    return;
+  }
 
-  m_impl->registry.ForeachLiveWindow(m_impl->dataHandle, [&](auto& cbdata) {
-    if (!cbdata.sendable || cbdata.parent) return;
+  wpi::SendableRegistry::ForeachLiveWindow(inst.dataHandle, [&](auto& cbdata) {
+    if (!cbdata.sendable || cbdata.parent) {
+      return;
+    }
 
-    if (!cbdata.data) cbdata.data = std::make_shared<Impl::Component>();
+    if (!cbdata.data) {
+      cbdata.data = std::make_shared<Component>();
+    }
 
-    auto& comp = *std::static_pointer_cast<Impl::Component>(cbdata.data);
+    auto& comp = *std::static_pointer_cast<Component>(cbdata.data);
 
-    if (!m_impl->liveWindowEnabled && !comp.telemetryEnabled) return;
+    if (!inst.liveWindowEnabled && !comp.telemetryEnabled) {
+      return;
+    }
 
     if (comp.firstTime) {
       // By holding off creating the NetworkTable entries, it allows the
       // components to be redefined. This allows default sensor and actuator
       // values to be created that are replaced with the custom names from
       // users calling setName.
-      if (cbdata.name.empty()) return;
-      auto ssTable = m_impl->liveWindowTable->GetSubTable(cbdata.subsystem);
-      std::shared_ptr<NetworkTable> table;
+      if (cbdata.name.empty()) {
+        return;
+      }
+      auto ssTable = inst.liveWindowTable->GetSubTable(cbdata.subsystem);
+      std::shared_ptr<nt::NetworkTable> table;
       // Treat name==subsystem as top level of subsystem
-      if (cbdata.name == cbdata.subsystem)
+      if (cbdata.name == cbdata.subsystem) {
         table = ssTable;
-      else
+      } else {
         table = ssTable->GetSubTable(cbdata.name);
+      }
       table->GetEntry(".name").SetString(cbdata.name);
-      cbdata.builder.SetTable(table);
+      static_cast<SendableBuilderImpl&>(cbdata.builder).SetTable(table);
       cbdata.sendable->InitSendable(cbdata.builder);
       ssTable->GetEntry(".type").SetString("LW Subsystem");
 
       comp.firstTime = false;
     }
 
-    if (m_impl->startLiveWindow) cbdata.builder.StartLiveWindowMode();
-    cbdata.builder.UpdateTable();
+    if (inst.startLiveWindow) {
+      static_cast<SendableBuilderImpl&>(cbdata.builder).StartLiveWindowMode();
+    }
+    cbdata.builder.Update();
   });
 
-  m_impl->startLiveWindow = false;
+  inst.startLiveWindow = false;
 }
-
-LiveWindow::LiveWindow() : m_impl(new Impl) {}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp b/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp
new file mode 100644
index 0000000..81fa868
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/DMC60.cpp
@@ -0,0 +1,18 @@
+// 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.
+
+#include "frc/motorcontrol/DMC60.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+DMC60::DMC60(int channel) : PWMMotorController("DMC60", channel) {
+  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_DigilentDMC60, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp
new file mode 100644
index 0000000..c68ae0c
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/Jaguar.cpp
@@ -0,0 +1,18 @@
+// 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.
+
+#include "frc/motorcontrol/Jaguar.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+Jaguar::Jaguar(int channel) : PWMMotorController("Jaguar", channel) {
+  m_pwm.SetBounds(2.31, 1.55, 1.507, 1.454, 0.697);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_Jaguar, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp b/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp
new file mode 100644
index 0000000..b7f26d1
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/MotorControllerGroup.cpp
@@ -0,0 +1,69 @@
+// 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.
+
+#include "frc/motorcontrol/MotorControllerGroup.h"
+
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
+
+using namespace frc;
+
+// Can't use a delegated constructor here because of an MSVC bug.
+// https://developercommunity.visualstudio.com/content/problem/583/compiler-bug-with-delegating-a-constructor.html
+
+MotorControllerGroup::MotorControllerGroup(
+    std::vector<std::reference_wrapper<MotorController>>&& motorControllers)
+    : m_motorControllers(std::move(motorControllers)) {
+  Initialize();
+}
+
+void MotorControllerGroup::Initialize() {
+  for (auto& motorController : m_motorControllers) {
+    wpi::SendableRegistry::AddChild(this, &motorController.get());
+  }
+  static int instances = 0;
+  ++instances;
+  wpi::SendableRegistry::Add(this, "MotorControllerGroup", instances);
+}
+
+void MotorControllerGroup::Set(double speed) {
+  for (auto motorController : m_motorControllers) {
+    motorController.get().Set(m_isInverted ? -speed : speed);
+  }
+}
+
+double MotorControllerGroup::Get() const {
+  if (!m_motorControllers.empty()) {
+    return m_motorControllers.front().get().Get() * (m_isInverted ? -1 : 1);
+  }
+  return 0.0;
+}
+
+void MotorControllerGroup::SetInverted(bool isInverted) {
+  m_isInverted = isInverted;
+}
+
+bool MotorControllerGroup::GetInverted() const {
+  return m_isInverted;
+}
+
+void MotorControllerGroup::Disable() {
+  for (auto motorController : m_motorControllers) {
+    motorController.get().Disable();
+  }
+}
+
+void MotorControllerGroup::StopMotor() {
+  for (auto motorController : m_motorControllers) {
+    motorController.get().StopMotor();
+  }
+}
+
+void MotorControllerGroup::InitSendable(wpi::SendableBuilder& builder) {
+  builder.SetSmartDashboardType("Motor Controller");
+  builder.SetActuator(true);
+  builder.SetSafeState([=] { StopMotor(); });
+  builder.AddDoubleProperty(
+      "Value", [=] { return Get(); }, [=](double value) { Set(value); });
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp b/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp
new file mode 100644
index 0000000..21164eb
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/NidecBrushless.cpp
@@ -0,0 +1,79 @@
+// 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.
+
+#include "frc/motorcontrol/NidecBrushless.h"
+
+#include <fmt/format.h>
+#include <hal/FRCUsageReporting.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
+
+using namespace frc;
+
+NidecBrushless::NidecBrushless(int pwmChannel, int dioChannel)
+    : m_dio(dioChannel), m_pwm(pwmChannel) {
+  wpi::SendableRegistry::AddChild(this, &m_dio);
+  wpi::SendableRegistry::AddChild(this, &m_pwm);
+  SetExpiration(0_s);
+  SetSafetyEnabled(false);
+
+  // the dio controls the output (in PWM mode)
+  m_dio.SetPWMRate(15625);
+  m_dio.EnablePWM(0.5);
+
+  HAL_Report(HALUsageReporting::kResourceType_NidecBrushless, pwmChannel + 1);
+  wpi::SendableRegistry::AddLW(this, "Nidec Brushless", pwmChannel);
+}
+
+void NidecBrushless::Set(double speed) {
+  if (!m_disabled) {
+    m_speed = speed;
+    m_dio.UpdateDutyCycle(0.5 + 0.5 * (m_isInverted ? -speed : speed));
+    m_pwm.SetRaw(0xffff);
+  }
+  Feed();
+}
+
+double NidecBrushless::Get() const {
+  return m_speed;
+}
+
+void NidecBrushless::SetInverted(bool isInverted) {
+  m_isInverted = isInverted;
+}
+
+bool NidecBrushless::GetInverted() const {
+  return m_isInverted;
+}
+
+void NidecBrushless::Disable() {
+  m_disabled = true;
+  m_dio.UpdateDutyCycle(0.5);
+  m_pwm.SetDisabled();
+}
+
+void NidecBrushless::Enable() {
+  m_disabled = false;
+}
+
+void NidecBrushless::StopMotor() {
+  m_dio.UpdateDutyCycle(0.5);
+  m_pwm.SetDisabled();
+}
+
+std::string NidecBrushless::GetDescription() const {
+  return fmt::format("Nidec {}", GetChannel());
+}
+
+int NidecBrushless::GetChannel() const {
+  return m_pwm.GetChannel();
+}
+
+void NidecBrushless::InitSendable(wpi::SendableBuilder& builder) {
+  builder.SetSmartDashboardType("Nidec Brushless");
+  builder.SetActuator(true);
+  builder.SetSafeState([=] { StopMotor(); });
+  builder.AddDoubleProperty(
+      "Value", [=] { return Get(); }, [=](double value) { Set(value); });
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp
new file mode 100644
index 0000000..9829a75
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMMotorController.cpp
@@ -0,0 +1,56 @@
+// 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.
+
+#include "frc/motorcontrol/PWMMotorController.h"
+
+#include <fmt/format.h>
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
+
+using namespace frc;
+
+void PWMMotorController::Set(double speed) {
+  m_pwm.SetSpeed(m_isInverted ? -speed : speed);
+}
+
+double PWMMotorController::Get() const {
+  return m_pwm.GetSpeed() * (m_isInverted ? -1.0 : 1.0);
+}
+
+void PWMMotorController::SetInverted(bool isInverted) {
+  m_isInverted = isInverted;
+}
+
+bool PWMMotorController::GetInverted() const {
+  return m_isInverted;
+}
+
+void PWMMotorController::Disable() {
+  m_pwm.SetDisabled();
+}
+
+void PWMMotorController::StopMotor() {
+  Disable();
+}
+
+std::string PWMMotorController::GetDescription() const {
+  return fmt::format("PWM {}", GetChannel());
+}
+
+int PWMMotorController::GetChannel() const {
+  return m_pwm.GetChannel();
+}
+
+PWMMotorController::PWMMotorController(std::string_view name, int channel)
+    : m_pwm(channel, false) {
+  wpi::SendableRegistry::AddLW(this, name, channel);
+}
+
+void PWMMotorController::InitSendable(wpi::SendableBuilder& builder) {
+  builder.SetSmartDashboardType("Motor Controller");
+  builder.SetActuator(true);
+  builder.SetSafeState([=] { Disable(); });
+  builder.AddDoubleProperty(
+      "Value", [=] { return Get(); }, [=](double value) { Set(value); });
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp
new file mode 100644
index 0000000..608d452
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMSparkMax.cpp
@@ -0,0 +1,19 @@
+// 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.
+
+#include "frc/motorcontrol/PWMSparkMax.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+PWMSparkMax::PWMSparkMax(int channel)
+    : PWMMotorController("PWMSparkMax", channel) {
+  m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_RevSparkMaxPWM, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp
new file mode 100644
index 0000000..2c6982b
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonFX.cpp
@@ -0,0 +1,19 @@
+// 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.
+
+#include "frc/motorcontrol/PWMTalonFX.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+PWMTalonFX::PWMTalonFX(int channel)
+    : PWMMotorController("PWMTalonFX", channel) {
+  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_TalonFX, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp
new file mode 100644
index 0000000..b253412
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMTalonSRX.cpp
@@ -0,0 +1,19 @@
+// 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.
+
+#include "frc/motorcontrol/PWMTalonSRX.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+PWMTalonSRX::PWMTalonSRX(int channel)
+    : PWMMotorController("PWMTalonSRX", channel) {
+  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_PWMTalonSRX, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp
new file mode 100644
index 0000000..e558028
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMVenom.cpp
@@ -0,0 +1,18 @@
+// 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.
+
+#include "frc/motorcontrol/PWMVenom.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+PWMVenom::PWMVenom(int channel) : PWMMotorController("PWMVenom", channel) {
+  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_FusionVenom, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp b/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp
new file mode 100644
index 0000000..10ce992
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/PWMVictorSPX.cpp
@@ -0,0 +1,19 @@
+// 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.
+
+#include "frc/motorcontrol/PWMVictorSPX.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+PWMVictorSPX::PWMVictorSPX(int channel)
+    : PWMMotorController("PWMVictorSPX", channel) {
+  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_PWMVictorSPX, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp b/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp
new file mode 100644
index 0000000..3d5738f
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/SD540.cpp
@@ -0,0 +1,19 @@
+// 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.
+
+#include "frc/motorcontrol/SD540.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+SD540::SD540(int channel) : PWMMotorController("SD540", channel) {
+  m_pwm.SetBounds(2.05, 1.55, 1.50, 1.44, 0.94);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_MindsensorsSD540,
+             GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp
new file mode 100644
index 0000000..45394df
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/Spark.cpp
@@ -0,0 +1,18 @@
+// 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.
+
+#include "frc/motorcontrol/Spark.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+Spark::Spark(int channel) : PWMMotorController("Spark", channel) {
+  m_pwm.SetBounds(2.003, 1.55, 1.50, 1.46, 0.999);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_RevSPARK, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp
new file mode 100644
index 0000000..f4b3b69
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/Talon.cpp
@@ -0,0 +1,18 @@
+// 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.
+
+#include "frc/motorcontrol/Talon.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+Talon::Talon(int channel) : PWMMotorController("Talon", channel) {
+  m_pwm.SetBounds(2.037, 1.539, 1.513, 1.487, 0.989);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_Talon, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp b/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp
new file mode 100644
index 0000000..3ad29f7
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/Victor.cpp
@@ -0,0 +1,18 @@
+// 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.
+
+#include "frc/motorcontrol/Victor.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+Victor::Victor(int channel) : PWMMotorController("Victor", channel) {
+  m_pwm.SetBounds(2.027, 1.525, 1.507, 1.49, 1.026);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_2X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_Victor, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp b/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp
new file mode 100644
index 0000000..6dc888e
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/motorcontrol/VictorSP.cpp
@@ -0,0 +1,18 @@
+// 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.
+
+#include "frc/motorcontrol/VictorSP.h"
+
+#include <hal/FRCUsageReporting.h>
+
+using namespace frc;
+
+VictorSP::VictorSP(int channel) : PWMMotorController("VictorSP", channel) {
+  m_pwm.SetBounds(2.004, 1.52, 1.50, 1.48, 0.997);
+  m_pwm.SetPeriodMultiplier(PWM::kPeriodMultiplier_1X);
+  m_pwm.SetSpeed(0.0);
+  m_pwm.SetZeroLatch();
+
+  HAL_Report(HALUsageReporting::kResourceType_VictorSP, GetChannel() + 1);
+}
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ComplexWidget.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ComplexWidget.cpp
index 19e17bb..eaac173 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/ComplexWidget.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/ComplexWidget.cpp
@@ -1,42 +1,44 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/ComplexWidget.h"
 
-#include "frc/smartdashboard/Sendable.h"
+#include <wpi/sendable/Sendable.h>
+
+#include "frc/smartdashboard/SendableBuilderImpl.h"
 
 using namespace frc;
 
 ComplexWidget::ComplexWidget(ShuffleboardContainer& parent,
-                             const wpi::Twine& title, Sendable& sendable)
+                             std::string_view title, wpi::Sendable& sendable)
     : ShuffleboardValue(title),
       ShuffleboardWidget(parent, title),
       m_sendable(sendable) {}
 
+ComplexWidget::~ComplexWidget() = default;
+
 void ComplexWidget::EnableIfActuator() {
-  if (m_builder.IsActuator()) {
-    m_builder.StartLiveWindowMode();
+  if (m_builder && static_cast<SendableBuilderImpl&>(*m_builder).IsActuator()) {
+    static_cast<SendableBuilderImpl&>(*m_builder).StartLiveWindowMode();
   }
 }
 
 void ComplexWidget::DisableIfActuator() {
-  if (m_builder.IsActuator()) {
-    m_builder.StopLiveWindowMode();
+  if (m_builder && static_cast<SendableBuilderImpl&>(*m_builder).IsActuator()) {
+    static_cast<SendableBuilderImpl&>(*m_builder).StopLiveWindowMode();
   }
 }
 
 void ComplexWidget::BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
                               std::shared_ptr<nt::NetworkTable> metaTable) {
   BuildMetadata(metaTable);
-  if (!m_builderInit) {
-    m_builder.SetTable(parentTable->GetSubTable(GetTitle()));
-    m_sendable.InitSendable(m_builder);
-    m_builder.StartListeners();
-    m_builderInit = true;
+  if (!m_builder) {
+    m_builder = std::make_unique<SendableBuilderImpl>();
+    static_cast<SendableBuilderImpl&>(*m_builder)
+        .SetTable(parentTable->GetSubTable(GetTitle()));
+    m_sendable.InitSendable(static_cast<SendableBuilderImpl&>(*m_builder));
+    static_cast<SendableBuilderImpl&>(*m_builder).StartListeners();
   }
-  m_builder.UpdateTable();
+  m_builder->Update();
 }
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/LayoutType.cpp b/wpilibc/src/main/native/cpp/shuffleboard/LayoutType.cpp
index a21cad9..9c47efb 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/LayoutType.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/LayoutType.cpp
@@ -1,12 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/LayoutType.h"
 
 using namespace frc;
 
-wpi::StringRef LayoutType::GetLayoutName() const { return m_layoutName; }
+std::string_view LayoutType::GetLayoutName() const {
+  return m_layoutName;
+}
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/RecordingController.cpp b/wpilibc/src/main/native/cpp/shuffleboard/RecordingController.cpp
index d80001e..834c4be 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/RecordingController.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/RecordingController.cpp
@@ -1,13 +1,10 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/RecordingController.h"
 
-#include "frc/DriverStation.h"
+#include "frc/Errors.h"
 
 using namespace frc;
 using namespace frc::detail;
@@ -29,7 +26,7 @@
   m_recordingControlEntry.SetBoolean(false);
 }
 
-void RecordingController::SetRecordingFileNameFormat(wpi::StringRef format) {
+void RecordingController::SetRecordingFileNameFormat(std::string_view format) {
   m_recordingFileNameFormatEntry.SetString(format);
 }
 
@@ -38,12 +35,14 @@
 }
 
 void RecordingController::AddEventMarker(
-    wpi::StringRef name, wpi::StringRef description,
+    std::string_view name, std::string_view description,
     ShuffleboardEventImportance importance) {
   if (name.empty()) {
-    DriverStation::ReportError("Shuffleboard event name was not specified");
+    FRC_ReportError(err::Error, "{}",
+                    "Shuffleboard event name was not specified");
     return;
   }
   m_eventsTable->GetSubTable(name)->GetEntry("Info").SetStringArray(
-      {description, ShuffleboardEventImportanceName(importance)});
+      {std::string{description},
+       std::string{ShuffleboardEventImportanceName(importance)}});
 }
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/SendableCameraWrapper.cpp b/wpilibc/src/main/native/cpp/shuffleboard/SendableCameraWrapper.cpp
index 663cc19..1675c64 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/SendableCameraWrapper.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/SendableCameraWrapper.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/SendableCameraWrapper.h"
 
@@ -12,9 +9,8 @@
 #include <string>
 
 #include <wpi/DenseMap.h>
-
-#include "frc/smartdashboard/SendableBuilder.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include <wpi/sendable/SendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
 
 namespace frc {
 namespace detail {
@@ -24,12 +20,12 @@
   return wrappers[static_cast<int>(source)];
 }
 
-void AddToSendableRegistry(frc::Sendable* sendable, std::string name) {
-  SendableRegistry::GetInstance().Add(sendable, name);
+void AddToSendableRegistry(wpi::Sendable* sendable, std::string name) {
+  wpi::SendableRegistry::Add(sendable, name);
 }
 }  // namespace detail
 
-void SendableCameraWrapper::InitSendable(SendableBuilder& builder) {
+void SendableCameraWrapper::InitSendable(wpi::SendableBuilder& builder) {
   builder.AddStringProperty(
       ".ShuffleboardURI", [this] { return m_uri; }, nullptr);
 }
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/Shuffleboard.cpp b/wpilibc/src/main/native/cpp/shuffleboard/Shuffleboard.cpp
index 2d69847..16b404c 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/Shuffleboard.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/Shuffleboard.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/Shuffleboard.h"
 
@@ -13,15 +10,19 @@
 
 using namespace frc;
 
-void Shuffleboard::Update() { GetInstance().Update(); }
+void Shuffleboard::Update() {
+  GetInstance().Update();
+}
 
-ShuffleboardTab& Shuffleboard::GetTab(wpi::StringRef title) {
+ShuffleboardTab& Shuffleboard::GetTab(std::string_view title) {
   return GetInstance().GetTab(title);
 }
 
-void Shuffleboard::SelectTab(int index) { GetInstance().SelectTab(index); }
+void Shuffleboard::SelectTab(int index) {
+  GetInstance().SelectTab(index);
+}
 
-void Shuffleboard::SelectTab(wpi::StringRef title) {
+void Shuffleboard::SelectTab(std::string_view title) {
   GetInstance().SelectTab(title);
 }
 
@@ -39,9 +40,11 @@
   GetRecordingController().StartRecording();
 }
 
-void Shuffleboard::StopRecording() { GetRecordingController().StopRecording(); }
+void Shuffleboard::StopRecording() {
+  GetRecordingController().StopRecording();
+}
 
-void Shuffleboard::SetRecordingFileNameFormat(wpi::StringRef format) {
+void Shuffleboard::SetRecordingFileNameFormat(std::string_view format) {
   GetRecordingController().SetRecordingFileNameFormat(format);
 }
 
@@ -49,13 +52,13 @@
   GetRecordingController().ClearRecordingFileNameFormat();
 }
 
-void Shuffleboard::AddEventMarker(wpi::StringRef name,
-                                  wpi::StringRef description,
+void Shuffleboard::AddEventMarker(std::string_view name,
+                                  std::string_view description,
                                   ShuffleboardEventImportance importance) {
   GetRecordingController().AddEventMarker(name, description, importance);
 }
 
-void Shuffleboard::AddEventMarker(wpi::StringRef name,
+void Shuffleboard::AddEventMarker(std::string_view name,
                                   ShuffleboardEventImportance importance) {
   AddEventMarker(name, "", importance);
 }
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardComponentBase.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardComponentBase.cpp
index 7617333..940f6ab 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardComponentBase.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardComponentBase.cpp
@@ -1,27 +1,18 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/ShuffleboardComponentBase.h"
 
-#include <wpi/SmallVector.h>
-
 using namespace frc;
 
 ShuffleboardComponentBase::ShuffleboardComponentBase(
-    ShuffleboardContainer& parent, const wpi::Twine& title,
-    const wpi::Twine& type)
-    : ShuffleboardValue(title), m_parent(parent) {
-  wpi::SmallVector<char, 16> storage;
-  m_type = type.toStringRef(storage);
-}
+    ShuffleboardContainer& parent, std::string_view title,
+    std::string_view type)
+    : ShuffleboardValue(title), m_parent(parent), m_type(type) {}
 
-void ShuffleboardComponentBase::SetType(const wpi::Twine& type) {
-  wpi::SmallVector<char, 16> storage;
-  m_type = type.toStringRef(storage);
+void ShuffleboardComponentBase::SetType(std::string_view type) {
+  m_type = type;
   m_metadataDirty = true;
 }
 
@@ -68,7 +59,9 @@
   return m_parent;
 }
 
-const std::string& ShuffleboardComponentBase::GetType() const { return m_type; }
+const std::string& ShuffleboardComponentBase::GetType() const {
+  return m_type;
+}
 
 const wpi::StringMap<std::shared_ptr<nt::Value>>&
 ShuffleboardComponentBase::GetProperties() const {
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardContainer.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardContainer.cpp
index d73f635..004f7c5 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardContainer.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardContainer.cpp
@@ -1,20 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/ShuffleboardContainer.h"
 
-#include <wpi/SmallVector.h>
-#include <wpi/raw_ostream.h>
+#include <wpi/sendable/SendableRegistry.h>
 
+#include "frc/Errors.h"
 #include "frc/shuffleboard/ComplexWidget.h"
 #include "frc/shuffleboard/ShuffleboardComponent.h"
 #include "frc/shuffleboard/ShuffleboardLayout.h"
 #include "frc/shuffleboard/SimpleWidget.h"
-#include "frc/smartdashboard/SendableRegistry.h"
 
 using namespace frc;
 
@@ -24,7 +20,7 @@
   return layoutStrings[static_cast<int>(layout)];
 }
 
-ShuffleboardContainer::ShuffleboardContainer(const wpi::Twine& title)
+ShuffleboardContainer::ShuffleboardContainer(std::string_view title)
     : ShuffleboardValue(title) {}
 
 const std::vector<std::unique_ptr<ShuffleboardComponentBase>>&
@@ -32,41 +28,37 @@
   return m_components;
 }
 
-ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
+ShuffleboardLayout& ShuffleboardContainer::GetLayout(std::string_view title,
                                                      BuiltInLayouts type) {
   return GetLayout(title, GetStringFromBuiltInLayout(type));
 }
 
-ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
+ShuffleboardLayout& ShuffleboardContainer::GetLayout(std::string_view title,
                                                      const LayoutType& type) {
   return GetLayout(title, type.GetLayoutName());
 }
 
-ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title,
-                                                     const wpi::Twine& type) {
-  wpi::SmallVector<char, 16> storage;
-  auto titleRef = title.toStringRef(storage);
-  if (m_layouts.count(titleRef) == 0) {
-    auto layout = std::make_unique<ShuffleboardLayout>(*this, titleRef, type);
+ShuffleboardLayout& ShuffleboardContainer::GetLayout(std::string_view title,
+                                                     std::string_view type) {
+  if (m_layouts.count(title) == 0) {
+    auto layout = std::make_unique<ShuffleboardLayout>(*this, title, type);
     auto ptr = layout.get();
     m_components.emplace_back(std::move(layout));
-    m_layouts.insert(std::make_pair(titleRef, ptr));
+    m_layouts.insert(std::make_pair(title, ptr));
   }
-  return *m_layouts[titleRef];
+  return *m_layouts[title];
 }
 
-ShuffleboardLayout& ShuffleboardContainer::GetLayout(const wpi::Twine& title) {
-  wpi::SmallVector<char, 16> storage;
-  auto titleRef = title.toStringRef(storage);
-  if (m_layouts.count(titleRef) == 0) {
-    wpi_setWPIErrorWithContext(
-        InvalidParameter, "No layout with the given title has been defined");
+ShuffleboardLayout& ShuffleboardContainer::GetLayout(std::string_view title) {
+  if (m_layouts.count(title) == 0) {
+    throw FRC_MakeError(err::InvalidParameter,
+                        "No layout with title {} has been defined", title);
   }
-  return *m_layouts[titleRef];
+  return *m_layouts[title];
 }
 
-ComplexWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
-                                          Sendable& sendable) {
+ComplexWidget& ShuffleboardContainer::Add(std::string_view title,
+                                          wpi::Sendable& sendable) {
   CheckTitle(title);
   auto widget = std::make_unique<ComplexWidget>(*this, title, sendable);
   auto ptr = widget.get();
@@ -74,16 +66,16 @@
   return *ptr;
 }
 
-ComplexWidget& ShuffleboardContainer::Add(Sendable& sendable) {
-  auto name = SendableRegistry::GetInstance().GetName(&sendable);
+ComplexWidget& ShuffleboardContainer::Add(wpi::Sendable& sendable) {
+  auto name = wpi::SendableRegistry::GetName(&sendable);
   if (name.empty()) {
-    wpi::outs() << "Sendable must have a name\n";
+    FRC_ReportError(err::Error, "{}", "Sendable must have a name");
   }
   return Add(name, sendable);
 }
 
 SimpleWidget& ShuffleboardContainer::Add(
-    const wpi::Twine& title, std::shared_ptr<nt::Value> defaultValue) {
+    std::string_view title, std::shared_ptr<nt::Value> defaultValue) {
   CheckTitle(title);
 
   auto widget = std::make_unique<SimpleWidget>(*this, title);
@@ -93,48 +85,48 @@
   return *ptr;
 }
 
-SimpleWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
+SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
                                          bool defaultValue) {
   return Add(title, nt::Value::MakeBoolean(defaultValue));
 }
 
-SimpleWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
+SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
                                          double defaultValue) {
   return Add(title, nt::Value::MakeDouble(defaultValue));
 }
 
-SimpleWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
+SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
                                          int defaultValue) {
   return Add(title, nt::Value::MakeDouble(defaultValue));
 }
 
-SimpleWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
-                                         const wpi::Twine& defaultValue) {
+SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
+                                         std::string_view defaultValue) {
   return Add(title, nt::Value::MakeString(defaultValue));
 }
 
-SimpleWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
+SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
                                          const char* defaultValue) {
   return Add(title, nt::Value::MakeString(defaultValue));
 }
 
-SimpleWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
-                                         wpi::ArrayRef<bool> defaultValue) {
+SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
+                                         wpi::span<const bool> defaultValue) {
   return Add(title, nt::Value::MakeBooleanArray(defaultValue));
 }
 
-SimpleWidget& ShuffleboardContainer::Add(const wpi::Twine& title,
-                                         wpi::ArrayRef<double> defaultValue) {
+SimpleWidget& ShuffleboardContainer::Add(std::string_view title,
+                                         wpi::span<const double> defaultValue) {
   return Add(title, nt::Value::MakeDoubleArray(defaultValue));
 }
 
 SimpleWidget& ShuffleboardContainer::Add(
-    const wpi::Twine& title, wpi::ArrayRef<std::string> defaultValue) {
+    std::string_view title, wpi::span<const std::string> defaultValue) {
   return Add(title, nt::Value::MakeStringArray(defaultValue));
 }
 
 SuppliedValueWidget<std::string>& ShuffleboardContainer::AddString(
-    const wpi::Twine& title, std::function<std::string()> supplier) {
+    std::string_view title, std::function<std::string()> supplier) {
   static auto setter = [](nt::NetworkTableEntry entry, std::string value) {
     entry.SetString(value);
   };
@@ -148,7 +140,7 @@
 }
 
 SuppliedValueWidget<double>& ShuffleboardContainer::AddNumber(
-    const wpi::Twine& title, std::function<double()> supplier) {
+    std::string_view title, std::function<double()> supplier) {
   static auto setter = [](nt::NetworkTableEntry entry, double value) {
     entry.SetDouble(value);
   };
@@ -162,7 +154,7 @@
 }
 
 SuppliedValueWidget<bool>& ShuffleboardContainer::AddBoolean(
-    const wpi::Twine& title, std::function<bool()> supplier) {
+    std::string_view title, std::function<bool()> supplier) {
   static auto setter = [](nt::NetworkTableEntry entry, bool value) {
     entry.SetBoolean(value);
   };
@@ -177,7 +169,7 @@
 
 SuppliedValueWidget<std::vector<std::string>>&
 ShuffleboardContainer::AddStringArray(
-    const wpi::Twine& title,
+    std::string_view title,
     std::function<std::vector<std::string>()> supplier) {
   static auto setter = [](nt::NetworkTableEntry entry,
                           std::vector<std::string> value) {
@@ -193,7 +185,7 @@
 }
 
 SuppliedValueWidget<std::vector<double>>& ShuffleboardContainer::AddNumberArray(
-    const wpi::Twine& title, std::function<std::vector<double>()> supplier) {
+    std::string_view title, std::function<std::vector<double>()> supplier) {
   static auto setter = [](nt::NetworkTableEntry entry,
                           std::vector<double> value) {
     entry.SetDoubleArray(value);
@@ -208,7 +200,7 @@
 }
 
 SuppliedValueWidget<std::vector<int>>& ShuffleboardContainer::AddBooleanArray(
-    const wpi::Twine& title, std::function<std::vector<int>()> supplier) {
+    std::string_view title, std::function<std::vector<int>()> supplier) {
   static auto setter = [](nt::NetworkTableEntry entry, std::vector<int> value) {
     entry.SetBooleanArray(value);
   };
@@ -221,14 +213,14 @@
   return *ptr;
 }
 
-SuppliedValueWidget<wpi::StringRef>& ShuffleboardContainer::AddRaw(
-    const wpi::Twine& title, std::function<wpi::StringRef()> supplier) {
-  static auto setter = [](nt::NetworkTableEntry entry, wpi::StringRef value) {
+SuppliedValueWidget<std::string_view>& ShuffleboardContainer::AddRaw(
+    std::string_view title, std::function<std::string_view()> supplier) {
+  static auto setter = [](nt::NetworkTableEntry entry, std::string_view value) {
     entry.SetRaw(value);
   };
 
   CheckTitle(title);
-  auto widget = std::make_unique<SuppliedValueWidget<wpi::StringRef>>(
+  auto widget = std::make_unique<SuppliedValueWidget<std::string_view>>(
       *this, title, supplier, setter);
   auto ptr = widget.get();
   m_components.emplace_back(std::move(widget));
@@ -236,44 +228,44 @@
 }
 
 SimpleWidget& ShuffleboardContainer::AddPersistent(
-    const wpi::Twine& title, std::shared_ptr<nt::Value> defaultValue) {
+    std::string_view title, std::shared_ptr<nt::Value> defaultValue) {
   auto& widget = Add(title, defaultValue);
   widget.GetEntry().SetPersistent();
   return widget;
 }
 
-SimpleWidget& ShuffleboardContainer::AddPersistent(const wpi::Twine& title,
+SimpleWidget& ShuffleboardContainer::AddPersistent(std::string_view title,
                                                    bool defaultValue) {
   return AddPersistent(title, nt::Value::MakeBoolean(defaultValue));
 }
 
-SimpleWidget& ShuffleboardContainer::AddPersistent(const wpi::Twine& title,
+SimpleWidget& ShuffleboardContainer::AddPersistent(std::string_view title,
                                                    double defaultValue) {
   return AddPersistent(title, nt::Value::MakeDouble(defaultValue));
 }
 
-SimpleWidget& ShuffleboardContainer::AddPersistent(const wpi::Twine& title,
+SimpleWidget& ShuffleboardContainer::AddPersistent(std::string_view title,
                                                    int defaultValue) {
   return AddPersistent(title, nt::Value::MakeDouble(defaultValue));
 }
 
 SimpleWidget& ShuffleboardContainer::AddPersistent(
-    const wpi::Twine& title, const wpi::Twine& defaultValue) {
+    std::string_view title, std::string_view defaultValue) {
   return AddPersistent(title, nt::Value::MakeString(defaultValue));
 }
 
 SimpleWidget& ShuffleboardContainer::AddPersistent(
-    const wpi::Twine& title, wpi::ArrayRef<bool> defaultValue) {
+    std::string_view title, wpi::span<const bool> defaultValue) {
   return AddPersistent(title, nt::Value::MakeBooleanArray(defaultValue));
 }
 
 SimpleWidget& ShuffleboardContainer::AddPersistent(
-    const wpi::Twine& title, wpi::ArrayRef<double> defaultValue) {
+    std::string_view title, wpi::span<const double> defaultValue) {
   return AddPersistent(title, nt::Value::MakeDoubleArray(defaultValue));
 }
 
 SimpleWidget& ShuffleboardContainer::AddPersistent(
-    const wpi::Twine& title, wpi::ArrayRef<std::string> defaultValue) {
+    std::string_view title, wpi::span<const std::string> defaultValue) {
   return AddPersistent(title, nt::Value::MakeStringArray(defaultValue));
 }
 
@@ -289,12 +281,11 @@
   }
 }
 
-void ShuffleboardContainer::CheckTitle(const wpi::Twine& title) {
-  wpi::SmallVector<char, 16> storage;
-  auto titleRef = title.toStringRef(storage);
-  if (m_usedTitles.count(titleRef) > 0) {
-    wpi::errs() << "Title is already in use: " << title << "\n";
+void ShuffleboardContainer::CheckTitle(std::string_view title) {
+  std::string titleStr{title};
+  if (m_usedTitles.count(titleStr) > 0) {
+    FRC_ReportError(err::Error, "Title is already in use: {}", title);
     return;
   }
-  m_usedTitles.insert(titleRef);
+  m_usedTitles.insert(titleStr);
 }
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
index 3316b3e..083b4a2 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardInstance.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/ShuffleboardInstance.h"
 
@@ -18,7 +15,7 @@
 using namespace frc::detail;
 
 struct ShuffleboardInstance::Impl {
-  wpi::StringMap<ShuffleboardTab> tabs;
+  wpi::StringMap<std::unique_ptr<ShuffleboardTab>> tabs;
 
   bool tabsChanged = false;
   std::shared_ptr<nt::NetworkTable> rootTable;
@@ -32,27 +29,28 @@
   HAL_Report(HALUsageReporting::kResourceType_Shuffleboard, 0);
 }
 
-ShuffleboardInstance::~ShuffleboardInstance() {}
+ShuffleboardInstance::~ShuffleboardInstance() = default;
 
-frc::ShuffleboardTab& ShuffleboardInstance::GetTab(wpi::StringRef title) {
+frc::ShuffleboardTab& ShuffleboardInstance::GetTab(std::string_view title) {
   if (m_impl->tabs.find(title) == m_impl->tabs.end()) {
-    m_impl->tabs.try_emplace(title, ShuffleboardTab(*this, title));
+    m_impl->tabs.try_emplace(title,
+                             std::make_unique<ShuffleboardTab>(*this, title));
     m_impl->tabsChanged = true;
   }
-  return m_impl->tabs.find(title)->second;
+  return *m_impl->tabs.find(title)->second;
 }
 
 void ShuffleboardInstance::Update() {
   if (m_impl->tabsChanged) {
     wpi::SmallVector<std::string, 16> tabTitles;
     for (auto& entry : m_impl->tabs) {
-      tabTitles.emplace_back(entry.second.GetTitle());
+      tabTitles.emplace_back(entry.second->GetTitle());
     }
     m_impl->rootMetaTable->GetEntry("Tabs").ForceSetStringArray(tabTitles);
     m_impl->tabsChanged = false;
   }
   for (auto& entry : m_impl->tabs) {
-    auto& tab = entry.second;
+    auto& tab = *entry.second;
     tab.BuildInto(m_impl->rootTable,
                   m_impl->rootMetaTable->GetSubTable(tab.GetTitle()));
   }
@@ -60,7 +58,7 @@
 
 void ShuffleboardInstance::EnableActuatorWidgets() {
   for (auto& entry : m_impl->tabs) {
-    auto& tab = entry.second;
+    auto& tab = *entry.second;
     for (auto& component : tab.GetComponents()) {
       component->EnableIfActuator();
     }
@@ -69,7 +67,7 @@
 
 void ShuffleboardInstance::DisableActuatorWidgets() {
   for (auto& entry : m_impl->tabs) {
-    auto& tab = entry.second;
+    auto& tab = *entry.second;
     for (auto& component : tab.GetComponents()) {
       component->DisableIfActuator();
     }
@@ -80,6 +78,6 @@
   m_impl->rootMetaTable->GetEntry("Selected").ForceSetDouble(index);
 }
 
-void ShuffleboardInstance::SelectTab(wpi::StringRef title) {
+void ShuffleboardInstance::SelectTab(std::string_view title) {
   m_impl->rootMetaTable->GetEntry("Selected").ForceSetString(title);
 }
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardLayout.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardLayout.cpp
index 1cbfb80..c4c933e 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardLayout.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardLayout.cpp
@@ -1,17 +1,14 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/ShuffleboardLayout.h"
 
 using namespace frc;
 
 ShuffleboardLayout::ShuffleboardLayout(ShuffleboardContainer& parent,
-                                       const wpi::Twine& title,
-                                       const wpi::Twine& type)
+                                       std::string_view title,
+                                       std::string_view type)
     : ShuffleboardValue(title),
       ShuffleboardComponent(parent, title, type),
       ShuffleboardContainer(title) {
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardTab.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardTab.cpp
index 7a8338e..61e0e45 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardTab.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardTab.cpp
@@ -1,18 +1,17 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/ShuffleboardTab.h"
 
 using namespace frc;
 
-ShuffleboardTab::ShuffleboardTab(ShuffleboardRoot& root, wpi::StringRef title)
+ShuffleboardTab::ShuffleboardTab(ShuffleboardRoot& root, std::string_view title)
     : ShuffleboardValue(title), ShuffleboardContainer(title), m_root(root) {}
 
-ShuffleboardRoot& ShuffleboardTab::GetRoot() { return m_root; }
+ShuffleboardRoot& ShuffleboardTab::GetRoot() {
+  return m_root;
+}
 
 void ShuffleboardTab::BuildInto(std::shared_ptr<nt::NetworkTable> parentTable,
                                 std::shared_ptr<nt::NetworkTable> metaTable) {
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardWidget.cpp b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardWidget.cpp
index 3b79a9c..81f6671 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardWidget.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/ShuffleboardWidget.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/ShuffleboardWidget.h"
 
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/SimpleWidget.cpp b/wpilibc/src/main/native/cpp/shuffleboard/SimpleWidget.cpp
index 89af25d..390c9c4 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/SimpleWidget.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/SimpleWidget.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/SimpleWidget.h"
 
@@ -14,7 +11,7 @@
 using namespace frc;
 
 SimpleWidget::SimpleWidget(ShuffleboardContainer& parent,
-                           const wpi::Twine& title)
+                           std::string_view title)
     : ShuffleboardValue(title), ShuffleboardWidget(parent, title), m_entry() {}
 
 nt::NetworkTableEntry SimpleWidget::GetEntry() {
diff --git a/wpilibc/src/main/native/cpp/shuffleboard/WidgetType.cpp b/wpilibc/src/main/native/cpp/shuffleboard/WidgetType.cpp
index cb73d30..3062cba 100644
--- a/wpilibc/src/main/native/cpp/shuffleboard/WidgetType.cpp
+++ b/wpilibc/src/main/native/cpp/shuffleboard/WidgetType.cpp
@@ -1,12 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/shuffleboard/WidgetType.h"
 
 using namespace frc;
 
-wpi::StringRef WidgetType::GetWidgetName() const { return m_widgetName; }
+std::string_view WidgetType::GetWidgetName() const {
+  return m_widgetName;
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/ADXL345Sim.cpp b/wpilibc/src/main/native/cpp/simulation/ADXL345Sim.cpp
new file mode 100644
index 0000000..27f9c1f
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/simulation/ADXL345Sim.cpp
@@ -0,0 +1,38 @@
+// 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.
+
+#include "frc/simulation/ADXL345Sim.h"
+
+#include "frc/ADXL345_I2C.h"
+#include "frc/ADXL345_SPI.h"
+#include "frc/simulation/SimDeviceSim.h"
+
+using namespace frc::sim;
+
+ADXL345Sim::ADXL345Sim(const frc::ADXL345_I2C& accel) {
+  frc::sim::SimDeviceSim deviceSim{"Accel:ADXL345_I2C", accel.GetI2CPort(),
+                                   accel.GetI2CDeviceAddress()};
+  m_simX = deviceSim.GetDouble("x");
+  m_simY = deviceSim.GetDouble("y");
+  m_simZ = deviceSim.GetDouble("z");
+}
+
+ADXL345Sim::ADXL345Sim(const frc::ADXL345_SPI& accel) {
+  frc::sim::SimDeviceSim deviceSim{"Accel:ADXL345_SPI", accel.GetSpiPort()};
+  m_simX = deviceSim.GetDouble("x");
+  m_simY = deviceSim.GetDouble("y");
+  m_simZ = deviceSim.GetDouble("z");
+}
+
+void ADXL345Sim::SetX(double accel) {
+  m_simX.Set(accel);
+}
+
+void ADXL345Sim::SetY(double accel) {
+  m_simY.Set(accel);
+}
+
+void ADXL345Sim::SetZ(double accel) {
+  m_simZ.Set(accel);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/ADXL362Sim.cpp b/wpilibc/src/main/native/cpp/simulation/ADXL362Sim.cpp
new file mode 100644
index 0000000..ac5bb5a
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/simulation/ADXL362Sim.cpp
@@ -0,0 +1,29 @@
+// 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.
+
+#include "frc/simulation/ADXL362Sim.h"
+
+#include "frc/ADXL362.h"
+#include "frc/simulation/SimDeviceSim.h"
+
+using namespace frc::sim;
+
+ADXL362Sim::ADXL362Sim(const frc::ADXL362& accel) {
+  frc::sim::SimDeviceSim deviceSim{"Accel:ADXL362", accel.GetSpiPort()};
+  m_simX = deviceSim.GetDouble("x");
+  m_simY = deviceSim.GetDouble("y");
+  m_simZ = deviceSim.GetDouble("z");
+}
+
+void ADXL362Sim::SetX(double accel) {
+  m_simX.Set(accel);
+}
+
+void ADXL362Sim::SetY(double accel) {
+  m_simY.Set(accel);
+}
+
+void ADXL362Sim::SetZ(double accel) {
+  m_simZ.Set(accel);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/ADXRS450_GyroSim.cpp b/wpilibc/src/main/native/cpp/simulation/ADXRS450_GyroSim.cpp
index 74a5180..a21d1b7 100644
--- a/wpilibc/src/main/native/cpp/simulation/ADXRS450_GyroSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/ADXRS450_GyroSim.cpp
@@ -1,33 +1,24 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/ADXRS450_GyroSim.h"
 
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
-
 #include "frc/ADXRS450_Gyro.h"
 #include "frc/simulation/SimDeviceSim.h"
 
 using namespace frc::sim;
 
 ADXRS450_GyroSim::ADXRS450_GyroSim(const frc::ADXRS450_Gyro& gyro) {
-  wpi::SmallString<128> fullname;
-  wpi::raw_svector_ostream os(fullname);
-  os << "ADXRS450_Gyro" << '[' << gyro.GetPort() << ']';
-  frc::sim::SimDeviceSim deviceSim{fullname.c_str()};
-  m_simAngle = deviceSim.GetDouble("Angle");
-  m_simRate = deviceSim.GetDouble("Rate");
+  frc::sim::SimDeviceSim deviceSim{"Gyro:ADXRS450", gyro.GetPort()};
+  m_simAngle = deviceSim.GetDouble("angle_x");
+  m_simRate = deviceSim.GetDouble("rate_x");
 }
 
 void ADXRS450_GyroSim::SetAngle(units::degree_t angle) {
-  m_simAngle.Set(angle.to<double>());
+  m_simAngle.Set(angle.value());
 }
 
 void ADXRS450_GyroSim::SetRate(units::degrees_per_second_t rate) {
-  m_simRate.Set(rate.to<double>());
+  m_simRate.Set(rate.value());
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/AddressableLEDSim.cpp b/wpilibc/src/main/native/cpp/simulation/AddressableLEDSim.cpp
index 743c51d..8114bb3 100644
--- a/wpilibc/src/main/native/cpp/simulation/AddressableLEDSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/AddressableLEDSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/AddressableLEDSim.h"
 
@@ -23,8 +20,9 @@
 
 AddressableLEDSim AddressableLEDSim::CreateForChannel(int pwmChannel) {
   int index = HALSIM_FindAddressableLEDForChannel(pwmChannel);
-  if (index < 0)
+  if (index < 0) {
     throw std::out_of_range("no addressable LED found for PWM channel");
+  }
   return AddressableLEDSim{index};
 }
 
@@ -101,7 +99,7 @@
 }
 
 std::unique_ptr<CallbackStore> AddressableLEDSim::RegisterDataCallback(
-    NotifyCallback callback, bool initialNotify) {
+    ConstBufferCallback callback, bool initialNotify) {
   auto store = std::make_unique<CallbackStore>(
       m_index, -1, callback, &HALSIM_CancelAddressableLEDDataCallback);
   store->SetUid(HALSIM_RegisterAddressableLEDDataCallback(
diff --git a/wpilibc/src/main/native/cpp/simulation/AnalogEncoderSim.cpp b/wpilibc/src/main/native/cpp/simulation/AnalogEncoderSim.cpp
index 5fee737..049241e 100644
--- a/wpilibc/src/main/native/cpp/simulation/AnalogEncoderSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/AnalogEncoderSim.cpp
@@ -1,25 +1,16 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/AnalogEncoderSim.h"
 
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
-
 #include "frc/AnalogEncoder.h"
 #include "frc/simulation/SimDeviceSim.h"
 
 using namespace frc::sim;
 
 AnalogEncoderSim::AnalogEncoderSim(const frc::AnalogEncoder& encoder) {
-  wpi::SmallString<128> fullname;
-  wpi::raw_svector_ostream os(fullname);
-  os << "AnalogEncoder" << '[' << encoder.GetChannel() << ']';
-  frc::sim::SimDeviceSim deviceSim{fullname.c_str()};
+  frc::sim::SimDeviceSim deviceSim{"AnalogEncoder", encoder.GetChannel()};
   m_positionSim = deviceSim.GetDouble("Position");
 }
 
@@ -28,7 +19,7 @@
 }
 
 void AnalogEncoderSim::SetTurns(units::turn_t turns) {
-  m_positionSim.Set(turns.to<double>());
+  m_positionSim.Set(turns.value());
 }
 
 units::turn_t AnalogEncoderSim::GetTurns() {
diff --git a/wpilibc/src/main/native/cpp/simulation/AnalogGyroSim.cpp b/wpilibc/src/main/native/cpp/simulation/AnalogGyroSim.cpp
index b7cab6b..625a3c0 100644
--- a/wpilibc/src/main/native/cpp/simulation/AnalogGyroSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/AnalogGyroSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/AnalogGyroSim.h"
 
@@ -74,4 +71,6 @@
   HALSIM_SetAnalogGyroInitialized(m_index, initialized);
 }
 
-void AnalogGyroSim::ResetData() { HALSIM_ResetAnalogGyroData(m_index); }
+void AnalogGyroSim::ResetData() {
+  HALSIM_ResetAnalogGyroData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/AnalogInputSim.cpp b/wpilibc/src/main/native/cpp/simulation/AnalogInputSim.cpp
index 057a188..14a6596 100644
--- a/wpilibc/src/main/native/cpp/simulation/AnalogInputSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/AnalogInputSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/AnalogInputSim.h"
 
@@ -179,4 +176,6 @@
   HALSIM_SetAnalogInAccumulatorDeadband(m_index, accumulatorDeadband);
 }
 
-void AnalogInputSim::ResetData() { HALSIM_ResetAnalogInData(m_index); }
+void AnalogInputSim::ResetData() {
+  HALSIM_ResetAnalogInData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/AnalogOutputSim.cpp b/wpilibc/src/main/native/cpp/simulation/AnalogOutputSim.cpp
index 4de9082..c2b01cf 100644
--- a/wpilibc/src/main/native/cpp/simulation/AnalogOutputSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/AnalogOutputSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/AnalogOutputSim.h"
 
@@ -56,4 +53,6 @@
   HALSIM_SetAnalogOutInitialized(m_index, initialized);
 }
 
-void AnalogOutputSim::ResetData() { HALSIM_ResetAnalogOutData(m_index); }
+void AnalogOutputSim::ResetData() {
+  HALSIM_ResetAnalogOutData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/AnalogTriggerSim.cpp b/wpilibc/src/main/native/cpp/simulation/AnalogTriggerSim.cpp
index 3325827..09e0a8d 100644
--- a/wpilibc/src/main/native/cpp/simulation/AnalogTriggerSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/AnalogTriggerSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/AnalogTriggerSim.h"
 
@@ -23,7 +20,9 @@
 
 AnalogTriggerSim AnalogTriggerSim::CreateForChannel(int channel) {
   int index = HALSIM_FindAnalogTriggerForChannel(channel);
-  if (index < 0) throw std::out_of_range("no analog trigger found for channel");
+  if (index < 0) {
+    throw std::out_of_range("no analog trigger found for channel");
+  }
   return AnalogTriggerSim{index};
 }
 
@@ -86,4 +85,6 @@
   HALSIM_SetAnalogTriggerTriggerUpperBound(m_index, triggerUpperBound);
 }
 
-void AnalogTriggerSim::ResetData() { HALSIM_ResetAnalogTriggerData(m_index); }
+void AnalogTriggerSim::ResetData() {
+  HALSIM_ResetAnalogTriggerData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/BuiltInAccelerometerSim.cpp b/wpilibc/src/main/native/cpp/simulation/BuiltInAccelerometerSim.cpp
index 601ee6e..0f6aa88 100644
--- a/wpilibc/src/main/native/cpp/simulation/BuiltInAccelerometerSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/BuiltInAccelerometerSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/BuiltInAccelerometerSim.h"
 
diff --git a/wpilibc/src/main/native/cpp/simulation/CTREPCMSim.cpp b/wpilibc/src/main/native/cpp/simulation/CTREPCMSim.cpp
new file mode 100644
index 0000000..9c7450b
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/simulation/CTREPCMSim.cpp
@@ -0,0 +1,139 @@
+// 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.
+
+#include "frc/simulation/CTREPCMSim.h"
+
+#include <memory>
+#include <utility>
+
+#include <hal/simulation/CTREPCMData.h>
+
+#include "frc/SensorUtil.h"
+
+using namespace frc;
+using namespace frc::sim;
+
+CTREPCMSim::CTREPCMSim() : m_index{SensorUtil::GetDefaultCTREPCMModule()} {}
+
+CTREPCMSim::CTREPCMSim(int module) : m_index{module} {}
+
+CTREPCMSim::CTREPCMSim(const PneumaticsBase& pneumatics)
+    : m_index{pneumatics.GetModuleNumber()} {}
+
+std::unique_ptr<CallbackStore> CTREPCMSim::RegisterInitializedCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelCTREPCMInitializedCallback);
+  store->SetUid(HALSIM_RegisterCTREPCMInitializedCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool CTREPCMSim::GetInitialized() const {
+  return HALSIM_GetCTREPCMInitialized(m_index);
+}
+
+void CTREPCMSim::SetInitialized(bool solenoidInitialized) {
+  HALSIM_SetCTREPCMInitialized(m_index, solenoidInitialized);
+}
+
+std::unique_ptr<CallbackStore> CTREPCMSim::RegisterSolenoidOutputCallback(
+    int channel, NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, channel, -1, callback,
+      &HALSIM_CancelCTREPCMSolenoidOutputCallback);
+  store->SetUid(HALSIM_RegisterCTREPCMSolenoidOutputCallback(
+      m_index, channel, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool CTREPCMSim::GetSolenoidOutput(int channel) const {
+  return HALSIM_GetCTREPCMSolenoidOutput(m_index, channel);
+}
+
+void CTREPCMSim::SetSolenoidOutput(int channel, bool solenoidOutput) {
+  HALSIM_SetCTREPCMSolenoidOutput(m_index, channel, solenoidOutput);
+}
+
+std::unique_ptr<CallbackStore> CTREPCMSim::RegisterCompressorOnCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelCTREPCMCompressorOnCallback);
+  store->SetUid(HALSIM_RegisterCTREPCMCompressorOnCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool CTREPCMSim::GetCompressorOn() const {
+  return HALSIM_GetCTREPCMCompressorOn(m_index);
+}
+
+void CTREPCMSim::SetCompressorOn(bool compressorOn) {
+  HALSIM_SetCTREPCMCompressorOn(m_index, compressorOn);
+}
+
+std::unique_ptr<CallbackStore> CTREPCMSim::RegisterClosedLoopEnabledCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelCTREPCMClosedLoopEnabledCallback);
+  store->SetUid(HALSIM_RegisterCTREPCMClosedLoopEnabledCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool CTREPCMSim::GetClosedLoopEnabled() const {
+  return HALSIM_GetCTREPCMClosedLoopEnabled(m_index);
+}
+
+void CTREPCMSim::SetClosedLoopEnabled(bool closedLoopEnabled) {
+  HALSIM_SetCTREPCMClosedLoopEnabled(m_index, closedLoopEnabled);
+}
+
+std::unique_ptr<CallbackStore> CTREPCMSim::RegisterPressureSwitchCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelCTREPCMPressureSwitchCallback);
+  store->SetUid(HALSIM_RegisterCTREPCMPressureSwitchCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool CTREPCMSim::GetPressureSwitch() const {
+  return HALSIM_GetCTREPCMPressureSwitch(m_index);
+}
+
+void CTREPCMSim::SetPressureSwitch(bool pressureSwitch) {
+  HALSIM_SetCTREPCMPressureSwitch(m_index, pressureSwitch);
+}
+
+std::unique_ptr<CallbackStore> CTREPCMSim::RegisterCompressorCurrentCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelCTREPCMCompressorCurrentCallback);
+  store->SetUid(HALSIM_RegisterCTREPCMCompressorCurrentCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+double CTREPCMSim::GetCompressorCurrent() const {
+  return HALSIM_GetCTREPCMCompressorCurrent(m_index);
+}
+
+void CTREPCMSim::SetCompressorCurrent(double compressorCurrent) {
+  HALSIM_SetCTREPCMCompressorCurrent(m_index, compressorCurrent);
+}
+
+uint8_t CTREPCMSim::GetAllSolenoidOutputs() const {
+  uint8_t ret = 0;
+  HALSIM_GetCTREPCMAllSolenoids(m_index, &ret);
+  return ret;
+}
+
+void CTREPCMSim::SetAllSolenoidOutputs(uint8_t outputs) {
+  HALSIM_SetCTREPCMAllSolenoids(m_index, outputs);
+}
+
+void CTREPCMSim::ResetData() {
+  HALSIM_ResetCTREPCMData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/CallbackStore.cpp b/wpilibc/src/main/native/cpp/simulation/CallbackStore.cpp
index 5c38372..be071e9 100644
--- a/wpilibc/src/main/native/cpp/simulation/CallbackStore.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/CallbackStore.cpp
@@ -1,12 +1,11 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/CallbackStore.h"
 
+#include <utility>
+
 using namespace frc;
 using namespace frc::sim;
 
@@ -24,31 +23,35 @@
 
 CallbackStore::CallbackStore(int32_t i, NotifyCallback cb,
                              CancelCallbackNoIndexFunc ccf)
-    : index(i), callback(cb), cancelType(NoIndex) {
+    : index(i), callback(std::move(cb)), cancelType(NoIndex) {
   this->ccnif = ccf;
 }
 
 CallbackStore::CallbackStore(int32_t i, int32_t u, NotifyCallback cb,
                              CancelCallbackFunc ccf)
-    : index(i), uid(u), callback(cb), cancelType(Normal) {
+    : index(i), uid(u), callback(std::move(cb)), cancelType(Normal) {
   this->ccf = ccf;
 }
 
 CallbackStore::CallbackStore(int32_t i, int32_t c, int32_t u, NotifyCallback cb,
                              CancelCallbackChannelFunc ccf)
-    : index(i), channel(c), uid(u), callback(cb), cancelType(Channel) {
+    : index(i),
+      channel(c),
+      uid(u),
+      callback(std::move(cb)),
+      cancelType(Channel) {
   this->cccf = ccf;
 }
 
 CallbackStore::CallbackStore(int32_t i, ConstBufferCallback cb,
                              CancelCallbackNoIndexFunc ccf)
-    : index(i), constBufferCallback(cb), cancelType(NoIndex) {
+    : index(i), constBufferCallback(std::move(cb)), cancelType(NoIndex) {
   this->ccnif = ccf;
 }
 
 CallbackStore::CallbackStore(int32_t i, int32_t u, ConstBufferCallback cb,
                              CancelCallbackFunc ccf)
-    : index(i), uid(u), constBufferCallback(cb), cancelType(Normal) {
+    : index(i), uid(u), constBufferCallback(std::move(cb)), cancelType(Normal) {
   this->ccf = ccf;
 }
 
@@ -58,7 +61,7 @@
     : index(i),
       channel(c),
       uid(u),
-      constBufferCallback(cb),
+      constBufferCallback(std::move(cb)),
       cancelType(Channel) {
   this->cccf = ccf;
 }
@@ -77,4 +80,6 @@
   }
 }
 
-void CallbackStore::SetUid(int32_t uid) { this->uid = uid; }
+void CallbackStore::SetUid(int32_t uid) {
+  this->uid = uid;
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/DIOSim.cpp b/wpilibc/src/main/native/cpp/simulation/DIOSim.cpp
index 9330735..8b73b7c 100644
--- a/wpilibc/src/main/native/cpp/simulation/DIOSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DIOSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/DIOSim.h"
 
@@ -50,9 +47,13 @@
   return store;
 }
 
-bool DIOSim::GetValue() const { return HALSIM_GetDIOValue(m_index); }
+bool DIOSim::GetValue() const {
+  return HALSIM_GetDIOValue(m_index);
+}
 
-void DIOSim::SetValue(bool value) { HALSIM_SetDIOValue(m_index, value); }
+void DIOSim::SetValue(bool value) {
+  HALSIM_SetDIOValue(m_index, value);
+}
 
 std::unique_ptr<CallbackStore> DIOSim::RegisterPulseLengthCallback(
     NotifyCallback callback, bool initialNotify) {
@@ -80,7 +81,9 @@
   return store;
 }
 
-bool DIOSim::GetIsInput() const { return HALSIM_GetDIOIsInput(m_index); }
+bool DIOSim::GetIsInput() const {
+  return HALSIM_GetDIOIsInput(m_index);
+}
 
 void DIOSim::SetIsInput(bool isInput) {
   HALSIM_SetDIOIsInput(m_index, isInput);
@@ -95,10 +98,14 @@
   return store;
 }
 
-int DIOSim::GetFilterIndex() const { return HALSIM_GetDIOFilterIndex(m_index); }
+int DIOSim::GetFilterIndex() const {
+  return HALSIM_GetDIOFilterIndex(m_index);
+}
 
 void DIOSim::SetFilterIndex(int filterIndex) {
   HALSIM_SetDIOFilterIndex(m_index, filterIndex);
 }
 
-void DIOSim::ResetData() { HALSIM_ResetDIOData(m_index); }
+void DIOSim::ResetData() {
+  HALSIM_ResetDIOData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp b/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
index 51ed94a..0bbf9c3 100644
--- a/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DifferentialDrivetrainSim.cpp
@@ -1,44 +1,56 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/DifferentialDrivetrainSim.h"
 
 #include <frc/system/plant/LinearSystemId.h>
 
-#include "frc/system/RungeKutta.h"
+#include <utility>
+
+#include <wpi/MathExtras.h>
+
+#include "frc/RobotController.h"
+#include "frc/system/NumericalIntegration.h"
 
 using namespace frc;
 using namespace frc::sim;
 
 DifferentialDrivetrainSim::DifferentialDrivetrainSim(
-    const LinearSystem<2, 2, 2>& plant, units::meter_t trackWidth,
-    DCMotor driveMotor, double gearRatio, units::meter_t wheelRadius)
-    : m_plant(plant),
+    LinearSystem<2, 2, 2> plant, units::meter_t trackWidth, DCMotor driveMotor,
+    double gearRatio, units::meter_t wheelRadius,
+    const std::array<double, 7>& measurementStdDevs)
+    : m_plant(std::move(plant)),
       m_rb(trackWidth / 2.0),
       m_wheelRadius(wheelRadius),
       m_motor(driveMotor),
       m_originalGearing(gearRatio),
-      m_currentGearing(gearRatio) {
+      m_currentGearing(gearRatio),
+      m_measurementStdDevs(measurementStdDevs) {
   m_x.setZero();
   m_u.setZero();
+  m_y.setZero();
 }
 
 DifferentialDrivetrainSim::DifferentialDrivetrainSim(
     frc::DCMotor driveMotor, double gearing, units::kilogram_square_meter_t J,
     units::kilogram_t mass, units::meter_t wheelRadius,
-    units::meter_t trackWidth)
+    units::meter_t trackWidth, const std::array<double, 7>& measurementStdDevs)
     : DifferentialDrivetrainSim(
           frc::LinearSystemId::DrivetrainVelocitySystem(
               driveMotor, mass, wheelRadius, trackWidth / 2.0, J, gearing),
-          trackWidth, driveMotor, gearing, wheelRadius) {}
+          trackWidth, driveMotor, gearing, wheelRadius, measurementStdDevs) {}
+
+Eigen::Vector<double, 2> DifferentialDrivetrainSim::ClampInput(
+    const Eigen::Vector<double, 2>& u) {
+  return frc::NormalizeInputVector<2>(u,
+                                      frc::RobotController::GetInputVoltage());
+}
 
 void DifferentialDrivetrainSim::SetInputs(units::volt_t leftVoltage,
                                           units::volt_t rightVoltage) {
-  m_u << leftVoltage.to<double>(), rightVoltage.to<double>();
+  m_u << leftVoltage.value(), rightVoltage.value();
+  m_u = ClampInput(m_u);
 }
 
 void DifferentialDrivetrainSim::SetGearing(double newGearing) {
@@ -46,65 +58,79 @@
 }
 
 void DifferentialDrivetrainSim::Update(units::second_t dt) {
-  m_x = RungeKutta([this](auto& x, auto& u) { return Dynamics(x, u); }, m_x,
-                   m_u, dt);
-}
-
-double DifferentialDrivetrainSim::GetState(int state) const {
-  return m_x(state);
+  m_x = RK4([this](auto& x, auto& u) { return Dynamics(x, u); }, m_x, m_u, dt);
+  m_y = m_x + frc::MakeWhiteNoiseVector<7>(m_measurementStdDevs);
 }
 
 double DifferentialDrivetrainSim::GetGearing() const {
   return m_currentGearing;
 }
 
-Eigen::Matrix<double, 7, 1> DifferentialDrivetrainSim::GetState() const {
+Eigen::Vector<double, 7> DifferentialDrivetrainSim::GetOutput() const {
+  return m_y;
+}
+
+Eigen::Vector<double, 7> DifferentialDrivetrainSim::GetState() const {
   return m_x;
 }
 
+double DifferentialDrivetrainSim::GetOutput(int output) const {
+  return m_y(output);
+}
+
+double DifferentialDrivetrainSim::GetState(int state) const {
+  return m_x(state);
+}
+
 Rotation2d DifferentialDrivetrainSim::GetHeading() const {
-  return Rotation2d(units::radian_t(m_x(State::kHeading)));
+  return Rotation2d(units::radian_t(GetOutput(State::kHeading)));
 }
 
 Pose2d DifferentialDrivetrainSim::GetPose() const {
-  return Pose2d(units::meter_t(m_x(State::kX)), units::meter_t(m_x(State::kY)),
-                Rotation2d(units::radian_t(m_x(State::kHeading))));
+  return Pose2d(units::meter_t(GetOutput(State::kX)),
+                units::meter_t(GetOutput(State::kY)), GetHeading());
 }
 
-units::ampere_t DifferentialDrivetrainSim::GetCurrentDraw() const {
+units::ampere_t DifferentialDrivetrainSim::GetLeftCurrentDraw() const {
   auto loadIleft =
-      m_motor.Current(units::radians_per_second_t(m_x(State::kLeftVelocity) *
-                                                  m_currentGearing /
-                                                  m_wheelRadius.to<double>()),
-                      units::volt_t(m_u(0))) *
+      m_motor.Current(
+          units::radians_per_second_t(m_x(State::kLeftVelocity) *
+                                      m_currentGearing / m_wheelRadius.value()),
+          units::volt_t(m_u(0))) *
       wpi::sgn(m_u(0));
 
+  return loadIleft;
+}
+
+units::ampere_t DifferentialDrivetrainSim::GetRightCurrentDraw() const {
   auto loadIRight =
-      m_motor.Current(units::radians_per_second_t(m_x(State::kRightVelocity) *
-                                                  m_currentGearing /
-                                                  m_wheelRadius.to<double>()),
-                      units::volt_t(m_u(1))) *
+      m_motor.Current(
+          units::radians_per_second_t(m_x(State::kRightVelocity) *
+                                      m_currentGearing / m_wheelRadius.value()),
+          units::volt_t(m_u(1))) *
       wpi::sgn(m_u(1));
 
-  return loadIleft + loadIRight;
+  return loadIRight;
+}
+units::ampere_t DifferentialDrivetrainSim::GetCurrentDraw() const {
+  return GetLeftCurrentDraw() + GetRightCurrentDraw();
 }
 
 void DifferentialDrivetrainSim::SetState(
-    const Eigen::Matrix<double, 7, 1>& state) {
+    const Eigen::Vector<double, 7>& state) {
   m_x = state;
 }
 
 void DifferentialDrivetrainSim::SetPose(const frc::Pose2d& pose) {
-  m_x(State::kX) = pose.X().to<double>();
-  m_x(State::kY) = pose.Y().to<double>();
-  m_x(State::kHeading) = pose.Rotation().Radians().to<double>();
+  m_x(State::kX) = pose.X().value();
+  m_x(State::kY) = pose.Y().value();
+  m_x(State::kHeading) = pose.Rotation().Radians().value();
   m_x(State::kLeftPosition) = 0;
   m_x(State::kRightPosition) = 0;
 }
 
-Eigen::Matrix<double, 7, 1> DifferentialDrivetrainSim::Dynamics(
-    const Eigen::Matrix<double, 7, 1>& x,
-    const Eigen::Matrix<double, 2, 1>& u) {
+Eigen::Vector<double, 7> DifferentialDrivetrainSim::Dynamics(
+    const Eigen::Vector<double, 7>& x, const Eigen::Vector<double, 2>& u) {
   // Because G^2 can be factored out of A, we can divide by the old ratio
   // squared and multiply by the new ratio squared to get a new drivetrain
   // model.
@@ -123,12 +149,12 @@
 
   double v = (x(State::kLeftVelocity) + x(State::kRightVelocity)) / 2.0;
 
-  Eigen::Matrix<double, 7, 1> xdot;
+  Eigen::Vector<double, 7> xdot;
   xdot(0) = v * std::cos(x(State::kHeading));
   xdot(1) = v * std::sin(x(State::kHeading));
   xdot(2) =
       ((x(State::kRightVelocity) - x(State::kLeftVelocity)) / (2.0 * m_rb))
-          .to<double>();
+          .value();
   xdot.block<4, 1>(3, 0) = A * x.block<4, 1>(3, 0) + B * u;
   return xdot;
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/DigitalPWMSim.cpp b/wpilibc/src/main/native/cpp/simulation/DigitalPWMSim.cpp
index 2ee55ce..eb1e02e 100644
--- a/wpilibc/src/main/native/cpp/simulation/DigitalPWMSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DigitalPWMSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/DigitalPWMSim.h"
 
@@ -23,7 +20,9 @@
 
 DigitalPWMSim DigitalPWMSim::CreateForChannel(int channel) {
   int index = HALSIM_FindDigitalPWMForChannel(channel);
-  if (index < 0) throw std::out_of_range("no digital PWM found for channel");
+  if (index < 0) {
+    throw std::out_of_range("no digital PWM found for channel");
+  }
   return DigitalPWMSim{index};
 }
 
@@ -74,8 +73,14 @@
   return store;
 }
 
-int DigitalPWMSim::GetPin() const { return HALSIM_GetDigitalPWMPin(m_index); }
+int DigitalPWMSim::GetPin() const {
+  return HALSIM_GetDigitalPWMPin(m_index);
+}
 
-void DigitalPWMSim::SetPin(int pin) { HALSIM_SetDigitalPWMPin(m_index, pin); }
+void DigitalPWMSim::SetPin(int pin) {
+  HALSIM_SetDigitalPWMPin(m_index, pin);
+}
 
-void DigitalPWMSim::ResetData() { HALSIM_ResetDigitalPWMData(m_index); }
+void DigitalPWMSim::ResetData() {
+  HALSIM_ResetDigitalPWMData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/DriverStationSim.cpp b/wpilibc/src/main/native/cpp/simulation/DriverStationSim.cpp
index 8c0bf70..5f2c645 100644
--- a/wpilibc/src/main/native/cpp/simulation/DriverStationSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DriverStationSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/DriverStationSim.h"
 
@@ -27,7 +24,9 @@
   return store;
 }
 
-bool DriverStationSim::GetEnabled() { return HALSIM_GetDriverStationEnabled(); }
+bool DriverStationSim::GetEnabled() {
+  return HALSIM_GetDriverStationEnabled();
+}
 
 void DriverStationSim::SetEnabled(bool enabled) {
   HALSIM_SetDriverStationEnabled(enabled);
@@ -59,9 +58,13 @@
   return store;
 }
 
-bool DriverStationSim::GetTest() { return HALSIM_GetDriverStationTest(); }
+bool DriverStationSim::GetTest() {
+  return HALSIM_GetDriverStationTest();
+}
 
-void DriverStationSim::SetTest(bool test) { HALSIM_SetDriverStationTest(test); }
+void DriverStationSim::SetTest(bool test) {
+  HALSIM_SetDriverStationTest(test);
+}
 
 std::unique_ptr<CallbackStore> DriverStationSim::RegisterEStopCallback(
     NotifyCallback callback, bool initialNotify) {
@@ -72,7 +75,9 @@
   return store;
 }
 
-bool DriverStationSim::GetEStop() { return HALSIM_GetDriverStationEStop(); }
+bool DriverStationSim::GetEStop() {
+  return HALSIM_GetDriverStationEStop();
+}
 
 void DriverStationSim::SetEStop(bool eStop) {
   HALSIM_SetDriverStationEStop(eStop);
@@ -150,7 +155,7 @@
 
 void DriverStationSim::NotifyNewData() {
   HALSIM_NotifyDriverStationNewData();
-  DriverStation::GetInstance().WaitForData();
+  DriverStation::WaitForData();
 }
 
 void DriverStationSim::SetSendError(bool shouldSend) {
@@ -252,4 +257,6 @@
   HALSIM_SetReplayNumber(replayNumber);
 }
 
-void DriverStationSim::ResetData() { HALSIM_ResetDriverStationData(); }
+void DriverStationSim::ResetData() {
+  HALSIM_ResetDriverStationData();
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp b/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
index b12f00a..cb83ccb 100644
--- a/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DutyCycleEncoderSim.cpp
@@ -1,31 +1,23 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/DutyCycleEncoderSim.h"
 
-#include <wpi/SmallString.h>
-#include <wpi/raw_ostream.h>
-
 #include "frc/DutyCycleEncoder.h"
 #include "frc/simulation/SimDeviceSim.h"
 
 using namespace frc::sim;
 
 DutyCycleEncoderSim::DutyCycleEncoderSim(const frc::DutyCycleEncoder& encoder) {
-  wpi::SmallString<128> fullname;
-  wpi::raw_svector_ostream os(fullname);
-  os << "DutyCycleEncoder" << '[' << encoder.GetFPGAIndex() << ']';
-  frc::sim::SimDeviceSim deviceSim{fullname.c_str()};
-  m_simPosition = deviceSim.GetDouble("Position");
-  m_simDistancePerRotation = deviceSim.GetDouble("DistancePerRotation");
+  frc::sim::SimDeviceSim deviceSim{"DutyCycle:DutyCycleEncoder",
+                                   encoder.GetSourceChannel()};
+  m_simPosition = deviceSim.GetDouble("position");
+  m_simDistancePerRotation = deviceSim.GetDouble("distance_per_rot");
 }
 
 void DutyCycleEncoderSim::Set(units::turn_t turns) {
-  m_simPosition.Set(turns.to<double>());
+  m_simPosition.Set(turns.value());
 }
 
 void DutyCycleEncoderSim::SetDistance(double distance) {
diff --git a/wpilibc/src/main/native/cpp/simulation/DutyCycleSim.cpp b/wpilibc/src/main/native/cpp/simulation/DutyCycleSim.cpp
index 7ec19fd..29f2df4 100644
--- a/wpilibc/src/main/native/cpp/simulation/DutyCycleSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/DutyCycleSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/DutyCycleSim.h"
 
@@ -23,7 +20,9 @@
 
 DutyCycleSim DutyCycleSim::CreateForChannel(int channel) {
   int index = HALSIM_FindDutyCycleForChannel(channel);
-  if (index < 0) throw std::out_of_range("no duty cycle found for channel");
+  if (index < 0) {
+    throw std::out_of_range("no duty cycle found for channel");
+  }
   return DutyCycleSim{index};
 }
 
@@ -61,8 +60,8 @@
   return HALSIM_GetDutyCycleFrequency(m_index);
 }
 
-void DutyCycleSim::SetFrequency(int count) {
-  HALSIM_SetDutyCycleFrequency(m_index, count);
+void DutyCycleSim::SetFrequency(int frequency) {
+  HALSIM_SetDutyCycleFrequency(m_index, frequency);
 }
 
 std::unique_ptr<CallbackStore> DutyCycleSim::RegisterOutputCallback(
@@ -78,8 +77,10 @@
   return HALSIM_GetDutyCycleOutput(m_index);
 }
 
-void DutyCycleSim::SetOutput(double period) {
-  HALSIM_SetDutyCycleOutput(m_index, period);
+void DutyCycleSim::SetOutput(double output) {
+  HALSIM_SetDutyCycleOutput(m_index, output);
 }
 
-void DutyCycleSim::ResetData() { HALSIM_ResetDutyCycleData(m_index); }
+void DutyCycleSim::ResetData() {
+  HALSIM_ResetDutyCycleData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp b/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
index 89c09cb..a2d5c66 100644
--- a/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/ElevatorSim.cpp
@@ -1,16 +1,12 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/ElevatorSim.h"
 
 #include <wpi/MathExtras.h>
 
-#include "frc/StateSpaceUtil.h"
-#include "frc/system/RungeKutta.h"
+#include "frc/system/NumericalIntegration.h"
 #include "frc/system/plant/LinearSystemId.h"
 
 using namespace frc;
@@ -42,16 +38,24 @@
       m_maxHeight(maxHeight),
       m_gearing(gearing) {}
 
-bool ElevatorSim::HasHitLowerLimit(const Eigen::Matrix<double, 2, 1>& x) const {
-  return x(0) < m_minHeight.to<double>();
+bool ElevatorSim::WouldHitLowerLimit(units::meter_t elevatorHeight) const {
+  return elevatorHeight < m_minHeight;
 }
 
-bool ElevatorSim::HasHitUpperLimit(const Eigen::Matrix<double, 2, 1>& x) const {
-  return x(0) > m_maxHeight.to<double>();
+bool ElevatorSim::WouldHitUpperLimit(units::meter_t elevatorHeight) const {
+  return elevatorHeight > m_maxHeight;
+}
+
+bool ElevatorSim::HasHitLowerLimit() const {
+  return WouldHitLowerLimit(units::meter_t(m_y(0)));
+}
+
+bool ElevatorSim::HasHitUpperLimit() const {
+  return WouldHitUpperLimit(units::meter_t(m_y(0)));
 }
 
 units::meter_t ElevatorSim::GetPosition() const {
-  return units::meter_t{m_x(0)};
+  return units::meter_t{m_y(0)};
 }
 
 units::meters_per_second_t ElevatorSim::GetVelocity() const {
@@ -74,25 +78,25 @@
 }
 
 void ElevatorSim::SetInputVoltage(units::volt_t voltage) {
-  SetInput(frc::MakeMatrix<1, 1>(voltage.to<double>()));
+  SetInput(Eigen::Vector<double, 1>{voltage.value()});
 }
 
-Eigen::Matrix<double, 2, 1> ElevatorSim::UpdateX(
-    const Eigen::Matrix<double, 2, 1>& currentXhat,
-    const Eigen::Matrix<double, 1, 1>& u, units::second_t dt) {
-  auto updatedXhat = RungeKutta(
-      [&](const Eigen::Matrix<double, 2, 1>& x,
-          const Eigen::Matrix<double, 1, 1>& u_)
-          -> Eigen::Matrix<double, 2, 1> {
-        return m_plant.A() * x + m_plant.B() * u_ + MakeMatrix<2, 1>(0.0, -9.8);
+Eigen::Vector<double, 2> ElevatorSim::UpdateX(
+    const Eigen::Vector<double, 2>& currentXhat,
+    const Eigen::Vector<double, 1>& u, units::second_t dt) {
+  auto updatedXhat = RKDP(
+      [&](const Eigen::Vector<double, 2>& x,
+          const Eigen::Vector<double, 1>& u_) -> Eigen::Vector<double, 2> {
+        return m_plant.A() * x + m_plant.B() * u_ +
+               Eigen::Vector<double, 2>{0.0, -9.8};
       },
       currentXhat, u, dt);
   // Check for collision after updating x-hat.
-  if (HasHitLowerLimit(updatedXhat)) {
-    return MakeMatrix<2, 1>(m_minHeight.to<double>(), 0.0);
+  if (WouldHitLowerLimit(units::meter_t(updatedXhat(0)))) {
+    return Eigen::Vector<double, 2>{m_minHeight.value(), 0.0};
   }
-  if (HasHitUpperLimit(updatedXhat)) {
-    return MakeMatrix<2, 1>(m_maxHeight.to<double>(), 0.0);
+  if (WouldHitUpperLimit(units::meter_t(updatedXhat(0)))) {
+    return Eigen::Vector<double, 2>{m_maxHeight.value(), 0.0};
   }
   return updatedXhat;
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp b/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
index 5bc5d52..146328d 100644
--- a/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/EncoderSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/EncoderSim.h"
 
@@ -23,11 +20,15 @@
 
 EncoderSim EncoderSim::CreateForChannel(int channel) {
   int index = HALSIM_FindEncoderForChannel(channel);
-  if (index < 0) throw std::out_of_range("no encoder found for channel");
+  if (index < 0) {
+    throw std::out_of_range("no encoder found for channel");
+  }
   return EncoderSim{index};
 }
 
-EncoderSim EncoderSim::CreateForIndex(int index) { return EncoderSim{index}; }
+EncoderSim EncoderSim::CreateForIndex(int index) {
+  return EncoderSim{index};
+}
 
 std::unique_ptr<CallbackStore> EncoderSim::RegisterInitializedCallback(
     NotifyCallback callback, bool initialNotify) {
@@ -55,9 +56,13 @@
   return store;
 }
 
-int EncoderSim::GetCount() const { return HALSIM_GetEncoderCount(m_index); }
+int EncoderSim::GetCount() const {
+  return HALSIM_GetEncoderCount(m_index);
+}
 
-void EncoderSim::SetCount(int count) { HALSIM_SetEncoderCount(m_index, count); }
+void EncoderSim::SetCount(int count) {
+  HALSIM_SetEncoderCount(m_index, count);
+}
 
 std::unique_ptr<CallbackStore> EncoderSim::RegisterPeriodCallback(
     NotifyCallback callback, bool initialNotify) {
@@ -85,7 +90,9 @@
   return store;
 }
 
-bool EncoderSim::GetReset() const { return HALSIM_GetEncoderReset(m_index); }
+bool EncoderSim::GetReset() const {
+  return HALSIM_GetEncoderReset(m_index);
+}
 
 void EncoderSim::SetReset(bool reset) {
   HALSIM_SetEncoderReset(m_index, reset);
@@ -176,14 +183,22 @@
   HALSIM_SetEncoderDistancePerPulse(m_index, distancePerPulse);
 }
 
-void EncoderSim::ResetData() { HALSIM_ResetEncoderData(m_index); }
+void EncoderSim::ResetData() {
+  HALSIM_ResetEncoderData(m_index);
+}
 
 void EncoderSim::SetDistance(double distance) {
   HALSIM_SetEncoderDistance(m_index, distance);
 }
 
-double EncoderSim::GetDistance() { return HALSIM_GetEncoderDistance(m_index); }
+double EncoderSim::GetDistance() {
+  return HALSIM_GetEncoderDistance(m_index);
+}
 
-void EncoderSim::SetRate(double rate) { HALSIM_SetEncoderRate(m_index, rate); }
+void EncoderSim::SetRate(double rate) {
+  HALSIM_SetEncoderRate(m_index, rate);
+}
 
-double EncoderSim::GetRate() { return HALSIM_GetEncoderRate(m_index); }
+double EncoderSim::GetRate() {
+  return HALSIM_GetEncoderRate(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/Field2d.cpp b/wpilibc/src/main/native/cpp/simulation/Field2d.cpp
deleted file mode 100644
index a6098b6..0000000
--- a/wpilibc/src/main/native/cpp/simulation/Field2d.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/simulation/Field2d.h"
-
-using namespace frc;
-
-Field2d::Field2d() : m_device{"Field2D"} {
-  if (m_device) {
-    m_x = m_device.CreateDouble("x", false, 0.0);
-    m_y = m_device.CreateDouble("y", false, 0.0);
-    m_rot = m_device.CreateDouble("rot", false, 0.0);
-  }
-}
-
-void Field2d::SetRobotPose(const Pose2d& pose) {
-  if (m_device) {
-    auto& translation = pose.Translation();
-    m_x.Set(translation.X().to<double>());
-    m_y.Set(translation.Y().to<double>());
-    m_rot.Set(pose.Rotation().Degrees().to<double>());
-  } else {
-    m_pose = pose;
-  }
-}
-
-void Field2d::SetRobotPose(units::meter_t x, units::meter_t y,
-                           Rotation2d rotation) {
-  if (m_device) {
-    m_x.Set(x.to<double>());
-    m_y.Set(y.to<double>());
-    m_rot.Set(rotation.Degrees().to<double>());
-  } else {
-    m_pose = Pose2d{x, y, rotation};
-  }
-}
-
-Pose2d Field2d::GetRobotPose() {
-  if (m_device) {
-    return Pose2d{units::meter_t{m_x.Get()}, units::meter_t{m_y.Get()},
-                  Rotation2d{units::degree_t{m_rot.Get()}}};
-  } else {
-    return m_pose;
-  }
-}
diff --git a/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp b/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
index 65fc3c8..f4371b1 100644
--- a/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/FlywheelSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/FlywheelSim.h"
 
@@ -41,5 +38,5 @@
 }
 
 void FlywheelSim::SetInputVoltage(units::volt_t voltage) {
-  SetInput(frc::MakeMatrix<1, 1>(voltage.to<double>()));
+  SetInput(Eigen::Vector<double, 1>{voltage.value()});
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/GenericHIDSim.cpp b/wpilibc/src/main/native/cpp/simulation/GenericHIDSim.cpp
index 8df265a..f4981e9 100644
--- a/wpilibc/src/main/native/cpp/simulation/GenericHIDSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/GenericHIDSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/GenericHIDSim.h"
 
@@ -18,7 +15,9 @@
 
 GenericHIDSim::GenericHIDSim(int port) : m_port{port} {}
 
-void GenericHIDSim::NotifyNewData() { DriverStationSim::NotifyNewData(); }
+void GenericHIDSim::NotifyNewData() {
+  DriverStationSim::NotifyNewData();
+}
 
 void GenericHIDSim::SetRawButton(int button, bool value) {
   DriverStationSim::SetJoystickButton(m_port, button, value);
@@ -32,7 +31,9 @@
   DriverStationSim::SetJoystickPOV(m_port, pov, value);
 }
 
-void GenericHIDSim::SetPOV(int value) { SetPOV(0, value); }
+void GenericHIDSim::SetPOV(int value) {
+  SetPOV(0, value);
+}
 
 void GenericHIDSim::SetAxisCount(int count) {
   DriverStationSim::SetJoystickAxisCount(m_port, count);
diff --git a/wpilibc/src/main/native/cpp/simulation/JoystickSim.cpp b/wpilibc/src/main/native/cpp/simulation/JoystickSim.cpp
index 55f6089..d103822 100644
--- a/wpilibc/src/main/native/cpp/simulation/JoystickSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/JoystickSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/JoystickSim.h"
 
@@ -58,6 +55,10 @@
              value);
 }
 
-void JoystickSim::SetTrigger(bool state) { SetRawButton(1, state); }
+void JoystickSim::SetTrigger(bool state) {
+  SetRawButton(1, state);
+}
 
-void JoystickSim::SetTop(bool state) { SetRawButton(2, state); }
+void JoystickSim::SetTop(bool state) {
+  SetRawButton(2, state);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/Mechanism2D.cpp b/wpilibc/src/main/native/cpp/simulation/Mechanism2D.cpp
deleted file mode 100644
index 43178b1..0000000
--- a/wpilibc/src/main/native/cpp/simulation/Mechanism2D.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/simulation/Mechanism2D.h"
-
-#include <wpi/SmallString.h>
-#include <wpi/Twine.h>
-#include <wpi/raw_ostream.h>
-
-using namespace frc;
-
-Mechanism2D::Mechanism2D() : m_device{"Mechanism2D"} {}
-
-void Mechanism2D::SetLigamentAngle(const wpi::Twine& ligamentPath,
-                                   float angle) {
-  if (m_device) {
-    wpi::SmallString<64> fullPathBuf;
-    wpi::StringRef fullPath =
-        (ligamentPath + "/angle").toNullTerminatedStringRef(fullPathBuf);
-    if (!createdItems.count(fullPath)) {
-      createdItems[fullPath] =
-          m_device.CreateDouble(fullPath.data(), false, angle);
-    }
-    createdItems[fullPath].Set(angle);
-  }
-}
-
-void Mechanism2D::SetLigamentLength(const wpi::Twine& ligamentPath,
-                                    float length) {
-  if (m_device) {
-    wpi::SmallString<64> fullPathBuf;
-    wpi::StringRef fullPath =
-        (ligamentPath + "/length").toNullTerminatedStringRef(fullPathBuf);
-    if (!createdItems.count(fullPath)) {
-      createdItems[fullPath] =
-          m_device.CreateDouble(fullPath.data(), false, length);
-    }
-    createdItems[fullPath].Set(length);
-  }
-}
diff --git a/wpilibc/src/main/native/cpp/simulation/PCMSim.cpp b/wpilibc/src/main/native/cpp/simulation/PCMSim.cpp
deleted file mode 100644
index 5d0566a..0000000
--- a/wpilibc/src/main/native/cpp/simulation/PCMSim.cpp
+++ /dev/null
@@ -1,158 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/simulation/PCMSim.h"
-
-#include <memory>
-#include <utility>
-
-#include <hal/simulation/PCMData.h>
-
-#include "frc/Compressor.h"
-#include "frc/SensorUtil.h"
-
-using namespace frc;
-using namespace frc::sim;
-
-PCMSim::PCMSim() : m_index{SensorUtil::GetDefaultSolenoidModule()} {}
-
-PCMSim::PCMSim(int module) : m_index{module} {}
-
-PCMSim::PCMSim(const Compressor& compressor)
-    : m_index{compressor.GetModule()} {}
-
-std::unique_ptr<CallbackStore> PCMSim::RegisterSolenoidInitializedCallback(
-    int channel, NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, channel, -1, callback,
-      &HALSIM_CancelPCMSolenoidInitializedCallback);
-  store->SetUid(HALSIM_RegisterPCMSolenoidInitializedCallback(
-      m_index, channel, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-bool PCMSim::GetSolenoidInitialized(int channel) const {
-  return HALSIM_GetPCMSolenoidInitialized(m_index, channel);
-}
-
-void PCMSim::SetSolenoidInitialized(int channel, bool solenoidInitialized) {
-  HALSIM_SetPCMSolenoidInitialized(m_index, channel, solenoidInitialized);
-}
-
-std::unique_ptr<CallbackStore> PCMSim::RegisterSolenoidOutputCallback(
-    int channel, NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, channel, -1, callback, &HALSIM_CancelPCMSolenoidOutputCallback);
-  store->SetUid(HALSIM_RegisterPCMSolenoidOutputCallback(
-      m_index, channel, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-bool PCMSim::GetSolenoidOutput(int channel) const {
-  return HALSIM_GetPCMSolenoidOutput(m_index, channel);
-}
-
-void PCMSim::SetSolenoidOutput(int channel, bool solenoidOutput) {
-  HALSIM_SetPCMSolenoidOutput(m_index, channel, solenoidOutput);
-}
-
-std::unique_ptr<CallbackStore> PCMSim::RegisterCompressorInitializedCallback(
-    NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPCMCompressorInitializedCallback);
-  store->SetUid(HALSIM_RegisterPCMCompressorInitializedCallback(
-      m_index, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-bool PCMSim::GetCompressorInitialized() const {
-  return HALSIM_GetPCMCompressorInitialized(m_index);
-}
-
-void PCMSim::SetCompressorInitialized(bool compressorInitialized) {
-  HALSIM_SetPCMCompressorInitialized(m_index, compressorInitialized);
-}
-
-std::unique_ptr<CallbackStore> PCMSim::RegisterCompressorOnCallback(
-    NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPCMCompressorOnCallback);
-  store->SetUid(HALSIM_RegisterPCMCompressorOnCallback(
-      m_index, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-bool PCMSim::GetCompressorOn() const {
-  return HALSIM_GetPCMCompressorOn(m_index);
-}
-
-void PCMSim::SetCompressorOn(bool compressorOn) {
-  HALSIM_SetPCMCompressorOn(m_index, compressorOn);
-}
-
-std::unique_ptr<CallbackStore> PCMSim::RegisterClosedLoopEnabledCallback(
-    NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPCMClosedLoopEnabledCallback);
-  store->SetUid(HALSIM_RegisterPCMClosedLoopEnabledCallback(
-      m_index, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-bool PCMSim::GetClosedLoopEnabled() const {
-  return HALSIM_GetPCMClosedLoopEnabled(m_index);
-}
-
-void PCMSim::SetClosedLoopEnabled(bool closedLoopEnabled) {
-  HALSIM_SetPCMClosedLoopEnabled(m_index, closedLoopEnabled);
-}
-
-std::unique_ptr<CallbackStore> PCMSim::RegisterPressureSwitchCallback(
-    NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPCMPressureSwitchCallback);
-  store->SetUid(HALSIM_RegisterPCMPressureSwitchCallback(
-      m_index, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-bool PCMSim::GetPressureSwitch() const {
-  return HALSIM_GetPCMPressureSwitch(m_index);
-}
-
-void PCMSim::SetPressureSwitch(bool pressureSwitch) {
-  HALSIM_SetPCMPressureSwitch(m_index, pressureSwitch);
-}
-
-std::unique_ptr<CallbackStore> PCMSim::RegisterCompressorCurrentCallback(
-    NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPCMCompressorCurrentCallback);
-  store->SetUid(HALSIM_RegisterPCMCompressorCurrentCallback(
-      m_index, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-double PCMSim::GetCompressorCurrent() const {
-  return HALSIM_GetPCMCompressorCurrent(m_index);
-}
-
-void PCMSim::SetCompressorCurrent(double compressorCurrent) {
-  HALSIM_SetPCMCompressorCurrent(m_index, compressorCurrent);
-}
-
-uint8_t PCMSim::GetAllSolenoidOutputs() const {
-  uint8_t ret = 0;
-  HALSIM_GetPCMAllSolenoids(m_index, &ret);
-  return ret;
-}
-
-void PCMSim::SetAllSolenoidOutputs(uint8_t outputs) {
-  HALSIM_SetPCMAllSolenoids(m_index, outputs);
-}
-
-void PCMSim::ResetData() { HALSIM_ResetPCMData(m_index); }
diff --git a/wpilibc/src/main/native/cpp/simulation/PDPSim.cpp b/wpilibc/src/main/native/cpp/simulation/PDPSim.cpp
deleted file mode 100644
index 26d6a45..0000000
--- a/wpilibc/src/main/native/cpp/simulation/PDPSim.cpp
+++ /dev/null
@@ -1,98 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/simulation/PDPSim.h"
-
-#include <memory>
-#include <utility>
-
-#include <hal/simulation/PDPData.h>
-
-#include "frc/PowerDistributionPanel.h"
-
-using namespace frc;
-using namespace frc::sim;
-
-PDPSim::PDPSim(int module) : m_index{module} {}
-
-PDPSim::PDPSim(const PowerDistributionPanel& pdp) : m_index{pdp.GetModule()} {}
-
-std::unique_ptr<CallbackStore> PDPSim::RegisterInitializedCallback(
-    NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPDPInitializedCallback);
-  store->SetUid(HALSIM_RegisterPDPInitializedCallback(
-      m_index, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-bool PDPSim::GetInitialized() const {
-  return HALSIM_GetPDPInitialized(m_index);
-}
-
-void PDPSim::SetInitialized(bool initialized) {
-  HALSIM_SetPDPInitialized(m_index, initialized);
-}
-
-std::unique_ptr<CallbackStore> PDPSim::RegisterTemperatureCallback(
-    NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPDPTemperatureCallback);
-  store->SetUid(HALSIM_RegisterPDPTemperatureCallback(
-      m_index, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-double PDPSim::GetTemperature() const {
-  return HALSIM_GetPDPTemperature(m_index);
-}
-
-void PDPSim::SetTemperature(double temperature) {
-  HALSIM_SetPDPTemperature(m_index, temperature);
-}
-
-std::unique_ptr<CallbackStore> PDPSim::RegisterVoltageCallback(
-    NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, -1, callback, &HALSIM_CancelPDPVoltageCallback);
-  store->SetUid(HALSIM_RegisterPDPVoltageCallback(m_index, &CallbackStoreThunk,
-                                                  store.get(), initialNotify));
-  return store;
-}
-
-double PDPSim::GetVoltage() const { return HALSIM_GetPDPVoltage(m_index); }
-
-void PDPSim::SetVoltage(double voltage) {
-  HALSIM_SetPDPVoltage(m_index, voltage);
-}
-
-std::unique_ptr<CallbackStore> PDPSim::RegisterCurrentCallback(
-    int channel, NotifyCallback callback, bool initialNotify) {
-  auto store = std::make_unique<CallbackStore>(
-      m_index, channel, -1, callback, &HALSIM_CancelPDPCurrentCallback);
-  store->SetUid(HALSIM_RegisterPDPCurrentCallback(
-      m_index, channel, &CallbackStoreThunk, store.get(), initialNotify));
-  return store;
-}
-
-double PDPSim::GetCurrent(int channel) const {
-  return HALSIM_GetPDPCurrent(m_index, channel);
-}
-
-void PDPSim::SetCurrent(int channel, double current) {
-  HALSIM_SetPDPCurrent(m_index, channel, current);
-}
-
-void PDPSim::GetAllCurrents(double* currents) const {
-  HALSIM_GetPDPAllCurrents(m_index, currents);
-}
-
-void PDPSim::SetAllCurrents(const double* currents) {
-  HALSIM_SetPDPAllCurrents(m_index, currents);
-}
-
-void PDPSim::ResetData() { HALSIM_ResetPDPData(m_index); }
diff --git a/wpilibc/src/main/native/cpp/simulation/PS4ControllerSim.cpp b/wpilibc/src/main/native/cpp/simulation/PS4ControllerSim.cpp
new file mode 100644
index 0000000..3403755
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/simulation/PS4ControllerSim.cpp
@@ -0,0 +1,101 @@
+// 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.
+
+#include "frc/simulation/PS4ControllerSim.h"
+
+#include "frc/PS4Controller.h"
+
+using namespace frc;
+using namespace frc::sim;
+
+PS4ControllerSim::PS4ControllerSim(const PS4Controller& joystick)
+    : GenericHIDSim{joystick} {
+  SetAxisCount(6);
+  SetButtonCount(14);
+}
+
+PS4ControllerSim::PS4ControllerSim(int port) : GenericHIDSim{port} {
+  SetAxisCount(6);
+  SetButtonCount(14);
+}
+
+void PS4ControllerSim::SetLeftX(double value) {
+  SetRawAxis(PS4Controller::Axis::kLeftX, value);
+}
+
+void PS4ControllerSim::SetRightX(double value) {
+  SetRawAxis(PS4Controller::Axis::kRightX, value);
+}
+
+void PS4ControllerSim::SetLeftY(double value) {
+  SetRawAxis(PS4Controller::Axis::kLeftY, value);
+}
+
+void PS4ControllerSim::SetRightY(double value) {
+  SetRawAxis(PS4Controller::Axis::kRightY, value);
+}
+
+void PS4ControllerSim::SetL2Axis(double value) {
+  SetRawAxis(PS4Controller::Axis::kL2, value);
+}
+
+void PS4ControllerSim::SetR2Axis(double value) {
+  SetRawAxis(PS4Controller::Axis::kR2, value);
+}
+
+void PS4ControllerSim::SetSquareButton(bool value) {
+  SetRawButton(PS4Controller::Button::kSquare, value);
+}
+
+void PS4ControllerSim::SetCrossButton(bool value) {
+  SetRawButton(PS4Controller::Button::kCross, value);
+}
+
+void PS4ControllerSim::SetCircleButton(bool value) {
+  SetRawButton(PS4Controller::Button::kCircle, value);
+}
+
+void PS4ControllerSim::SetTriangleButton(bool value) {
+  SetRawButton(PS4Controller::Button::kTriangle, value);
+}
+
+void PS4ControllerSim::SetL1Button(bool value) {
+  SetRawButton(PS4Controller::Button::kL1, value);
+}
+
+void PS4ControllerSim::SetR1Button(bool value) {
+  SetRawButton(PS4Controller::Button::kR1, value);
+}
+
+void PS4ControllerSim::SetL2Button(bool value) {
+  SetRawButton(PS4Controller::Button::kL2, value);
+}
+
+void PS4ControllerSim::SetR2Button(bool value) {
+  SetRawButton(PS4Controller::Button::kR2, value);
+}
+
+void PS4ControllerSim::SetShareButton(bool value) {
+  SetRawButton(PS4Controller::Button::kShare, value);
+}
+
+void PS4ControllerSim::SetOptionsButton(bool value) {
+  SetRawButton(PS4Controller::Button::kOptions, value);
+}
+
+void PS4ControllerSim::SetL3Button(bool value) {
+  SetRawButton(PS4Controller::Button::kL3, value);
+}
+
+void PS4ControllerSim::SetR3Button(bool value) {
+  SetRawButton(PS4Controller::Button::kR3, value);
+}
+
+void PS4ControllerSim::SetPSButton(bool value) {
+  SetRawButton(PS4Controller::Button::kPS, value);
+}
+
+void PS4ControllerSim::SetTouchpad(bool value) {
+  SetRawButton(PS4Controller::Button::kTouchpad, value);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp b/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
index 2fae8fb..f5d69db 100644
--- a/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/PWMSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/PWMSim.h"
 
@@ -47,7 +44,9 @@
   return store;
 }
 
-int PWMSim::GetRawValue() const { return HALSIM_GetPWMRawValue(m_index); }
+int PWMSim::GetRawValue() const {
+  return HALSIM_GetPWMRawValue(m_index);
+}
 
 void PWMSim::SetRawValue(int rawValue) {
   HALSIM_SetPWMRawValue(m_index, rawValue);
@@ -62,9 +61,13 @@
   return store;
 }
 
-double PWMSim::GetSpeed() const { return HALSIM_GetPWMSpeed(m_index); }
+double PWMSim::GetSpeed() const {
+  return HALSIM_GetPWMSpeed(m_index);
+}
 
-void PWMSim::SetSpeed(double speed) { HALSIM_SetPWMSpeed(m_index, speed); }
+void PWMSim::SetSpeed(double speed) {
+  HALSIM_SetPWMSpeed(m_index, speed);
+}
 
 std::unique_ptr<CallbackStore> PWMSim::RegisterPositionCallback(
     NotifyCallback callback, bool initialNotify) {
@@ -75,7 +78,9 @@
   return store;
 }
 
-double PWMSim::GetPosition() const { return HALSIM_GetPWMPosition(m_index); }
+double PWMSim::GetPosition() const {
+  return HALSIM_GetPWMPosition(m_index);
+}
 
 void PWMSim::SetPosition(double position) {
   HALSIM_SetPWMPosition(m_index, position);
@@ -90,7 +95,9 @@
   return store;
 }
 
-int PWMSim::GetPeriodScale() const { return HALSIM_GetPWMPeriodScale(m_index); }
+int PWMSim::GetPeriodScale() const {
+  return HALSIM_GetPWMPeriodScale(m_index);
+}
 
 void PWMSim::SetPeriodScale(int periodScale) {
   HALSIM_SetPWMPeriodScale(m_index, periodScale);
@@ -105,10 +112,14 @@
   return store;
 }
 
-bool PWMSim::GetZeroLatch() const { return HALSIM_GetPWMZeroLatch(m_index); }
+bool PWMSim::GetZeroLatch() const {
+  return HALSIM_GetPWMZeroLatch(m_index);
+}
 
 void PWMSim::SetZeroLatch(bool zeroLatch) {
   HALSIM_SetPWMZeroLatch(m_index, zeroLatch);
 }
 
-void PWMSim::ResetData() { HALSIM_ResetPWMData(m_index); }
+void PWMSim::ResetData() {
+  HALSIM_ResetPWMData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/PowerDistributionSim.cpp b/wpilibc/src/main/native/cpp/simulation/PowerDistributionSim.cpp
new file mode 100644
index 0000000..844a1b6
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/simulation/PowerDistributionSim.cpp
@@ -0,0 +1,105 @@
+// 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.
+
+#include "frc/simulation/PowerDistributionSim.h"
+
+#include <memory>
+#include <utility>
+
+#include <hal/simulation/PowerDistributionData.h>
+
+#include "frc/PowerDistribution.h"
+
+using namespace frc;
+using namespace frc::sim;
+
+PowerDistributionSim::PowerDistributionSim(int module) : m_index{module} {}
+
+PowerDistributionSim::PowerDistributionSim(const PowerDistribution& pdp)
+    : m_index{pdp.GetModule()} {}
+
+std::unique_ptr<CallbackStore>
+PowerDistributionSim::RegisterInitializedCallback(NotifyCallback callback,
+                                                  bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback,
+      &HALSIM_CancelPowerDistributionInitializedCallback);
+  store->SetUid(HALSIM_RegisterPowerDistributionInitializedCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool PowerDistributionSim::GetInitialized() const {
+  return HALSIM_GetPowerDistributionInitialized(m_index);
+}
+
+void PowerDistributionSim::SetInitialized(bool initialized) {
+  HALSIM_SetPowerDistributionInitialized(m_index, initialized);
+}
+
+std::unique_ptr<CallbackStore>
+PowerDistributionSim::RegisterTemperatureCallback(NotifyCallback callback,
+                                                  bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback,
+      &HALSIM_CancelPowerDistributionTemperatureCallback);
+  store->SetUid(HALSIM_RegisterPowerDistributionTemperatureCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+double PowerDistributionSim::GetTemperature() const {
+  return HALSIM_GetPowerDistributionTemperature(m_index);
+}
+
+void PowerDistributionSim::SetTemperature(double temperature) {
+  HALSIM_SetPowerDistributionTemperature(m_index, temperature);
+}
+
+std::unique_ptr<CallbackStore> PowerDistributionSim::RegisterVoltageCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelPowerDistributionVoltageCallback);
+  store->SetUid(HALSIM_RegisterPowerDistributionVoltageCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+double PowerDistributionSim::GetVoltage() const {
+  return HALSIM_GetPowerDistributionVoltage(m_index);
+}
+
+void PowerDistributionSim::SetVoltage(double voltage) {
+  HALSIM_SetPowerDistributionVoltage(m_index, voltage);
+}
+
+std::unique_ptr<CallbackStore> PowerDistributionSim::RegisterCurrentCallback(
+    int channel, NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, channel, -1, callback,
+      &HALSIM_CancelPowerDistributionCurrentCallback);
+  store->SetUid(HALSIM_RegisterPowerDistributionCurrentCallback(
+      m_index, channel, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+double PowerDistributionSim::GetCurrent(int channel) const {
+  return HALSIM_GetPowerDistributionCurrent(m_index, channel);
+}
+
+void PowerDistributionSim::SetCurrent(int channel, double current) {
+  HALSIM_SetPowerDistributionCurrent(m_index, channel, current);
+}
+
+void PowerDistributionSim::GetAllCurrents(double* currents, int length) const {
+  HALSIM_GetPowerDistributionAllCurrents(m_index, currents, length);
+}
+
+void PowerDistributionSim::SetAllCurrents(const double* currents, int length) {
+  HALSIM_SetPowerDistributionAllCurrents(m_index, currents, length);
+}
+
+void PowerDistributionSim::ResetData() {
+  HALSIM_ResetPowerDistributionData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/REVPHSim.cpp b/wpilibc/src/main/native/cpp/simulation/REVPHSim.cpp
new file mode 100644
index 0000000..dd6dba8
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/simulation/REVPHSim.cpp
@@ -0,0 +1,139 @@
+// 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.
+
+#include "frc/simulation/REVPHSim.h"
+
+#include <memory>
+#include <utility>
+
+#include <hal/simulation/REVPHData.h>
+
+#include "frc/SensorUtil.h"
+
+using namespace frc;
+using namespace frc::sim;
+
+REVPHSim::REVPHSim() : m_index{SensorUtil::GetDefaultREVPHModule()} {}
+
+REVPHSim::REVPHSim(int module) : m_index{module} {}
+
+REVPHSim::REVPHSim(const PneumaticsBase& pneumatics)
+    : m_index{pneumatics.GetModuleNumber()} {}
+
+std::unique_ptr<CallbackStore> REVPHSim::RegisterInitializedCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelREVPHInitializedCallback);
+  store->SetUid(HALSIM_RegisterREVPHInitializedCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool REVPHSim::GetInitialized() const {
+  return HALSIM_GetREVPHInitialized(m_index);
+}
+
+void REVPHSim::SetInitialized(bool solenoidInitialized) {
+  HALSIM_SetREVPHInitialized(m_index, solenoidInitialized);
+}
+
+std::unique_ptr<CallbackStore> REVPHSim::RegisterSolenoidOutputCallback(
+    int channel, NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, channel, -1, callback,
+      &HALSIM_CancelREVPHSolenoidOutputCallback);
+  store->SetUid(HALSIM_RegisterREVPHSolenoidOutputCallback(
+      m_index, channel, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool REVPHSim::GetSolenoidOutput(int channel) const {
+  return HALSIM_GetREVPHSolenoidOutput(m_index, channel);
+}
+
+void REVPHSim::SetSolenoidOutput(int channel, bool solenoidOutput) {
+  HALSIM_SetREVPHSolenoidOutput(m_index, channel, solenoidOutput);
+}
+
+std::unique_ptr<CallbackStore> REVPHSim::RegisterCompressorOnCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelREVPHCompressorOnCallback);
+  store->SetUid(HALSIM_RegisterREVPHCompressorOnCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool REVPHSim::GetCompressorOn() const {
+  return HALSIM_GetREVPHCompressorOn(m_index);
+}
+
+void REVPHSim::SetCompressorOn(bool compressorOn) {
+  HALSIM_SetREVPHCompressorOn(m_index, compressorOn);
+}
+
+std::unique_ptr<CallbackStore> REVPHSim::RegisterClosedLoopEnabledCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelREVPHClosedLoopEnabledCallback);
+  store->SetUid(HALSIM_RegisterREVPHClosedLoopEnabledCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool REVPHSim::GetClosedLoopEnabled() const {
+  return HALSIM_GetREVPHClosedLoopEnabled(m_index);
+}
+
+void REVPHSim::SetClosedLoopEnabled(bool closedLoopEnabled) {
+  HALSIM_SetREVPHClosedLoopEnabled(m_index, closedLoopEnabled);
+}
+
+std::unique_ptr<CallbackStore> REVPHSim::RegisterPressureSwitchCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelREVPHPressureSwitchCallback);
+  store->SetUid(HALSIM_RegisterREVPHPressureSwitchCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+bool REVPHSim::GetPressureSwitch() const {
+  return HALSIM_GetREVPHPressureSwitch(m_index);
+}
+
+void REVPHSim::SetPressureSwitch(bool pressureSwitch) {
+  HALSIM_SetREVPHPressureSwitch(m_index, pressureSwitch);
+}
+
+std::unique_ptr<CallbackStore> REVPHSim::RegisterCompressorCurrentCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      m_index, -1, callback, &HALSIM_CancelREVPHCompressorCurrentCallback);
+  store->SetUid(HALSIM_RegisterREVPHCompressorCurrentCallback(
+      m_index, &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+double REVPHSim::GetCompressorCurrent() const {
+  return HALSIM_GetREVPHCompressorCurrent(m_index);
+}
+
+void REVPHSim::SetCompressorCurrent(double compressorCurrent) {
+  HALSIM_SetREVPHCompressorCurrent(m_index, compressorCurrent);
+}
+
+uint8_t REVPHSim::GetAllSolenoidOutputs() const {
+  uint8_t ret = 0;
+  HALSIM_GetREVPHAllSolenoids(m_index, &ret);
+  return ret;
+}
+
+void REVPHSim::SetAllSolenoidOutputs(uint8_t outputs) {
+  HALSIM_SetREVPHAllSolenoids(m_index, outputs);
+}
+
+void REVPHSim::ResetData() {
+  HALSIM_ResetREVPHData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/RelaySim.cpp b/wpilibc/src/main/native/cpp/simulation/RelaySim.cpp
index 7f7aa65..84ce120 100644
--- a/wpilibc/src/main/native/cpp/simulation/RelaySim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/RelaySim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/RelaySim.h"
 
@@ -64,7 +61,9 @@
   return store;
 }
 
-bool RelaySim::GetForward() const { return HALSIM_GetRelayForward(m_index); }
+bool RelaySim::GetForward() const {
+  return HALSIM_GetRelayForward(m_index);
+}
 
 void RelaySim::SetForward(bool forward) {
   HALSIM_SetRelayForward(m_index, forward);
@@ -79,10 +78,14 @@
   return store;
 }
 
-bool RelaySim::GetReverse() const { return HALSIM_GetRelayReverse(m_index); }
+bool RelaySim::GetReverse() const {
+  return HALSIM_GetRelayReverse(m_index);
+}
 
 void RelaySim::SetReverse(bool reverse) {
   HALSIM_SetRelayReverse(m_index, reverse);
 }
 
-void RelaySim::ResetData() { HALSIM_ResetRelayData(m_index); }
+void RelaySim::ResetData() {
+  HALSIM_ResetRelayData(m_index);
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp b/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
index 0d1d104..dc2b557 100644
--- a/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/RoboRioSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/RoboRioSim.h"
 
@@ -24,7 +21,9 @@
   return store;
 }
 
-bool RoboRioSim::GetFPGAButton() { return HALSIM_GetRoboRioFPGAButton(); }
+bool RoboRioSim::GetFPGAButton() {
+  return HALSIM_GetRoboRioFPGAButton();
+}
 
 void RoboRioSim::SetFPGAButton(bool fPGAButton) {
   HALSIM_SetRoboRioFPGAButton(fPGAButton);
@@ -44,7 +43,7 @@
 }
 
 void RoboRioSim::SetVInVoltage(units::volt_t vInVoltage) {
-  HALSIM_SetRoboRioVInVoltage(vInVoltage.to<double>());
+  HALSIM_SetRoboRioVInVoltage(vInVoltage.value());
 }
 
 std::unique_ptr<CallbackStore> RoboRioSim::RegisterVInCurrentCallback(
@@ -61,7 +60,7 @@
 }
 
 void RoboRioSim::SetVInCurrent(units::ampere_t vInCurrent) {
-  HALSIM_SetRoboRioVInCurrent(vInCurrent.to<double>());
+  HALSIM_SetRoboRioVInCurrent(vInCurrent.value());
 }
 
 std::unique_ptr<CallbackStore> RoboRioSim::RegisterUserVoltage6VCallback(
@@ -78,7 +77,7 @@
 }
 
 void RoboRioSim::SetUserVoltage6V(units::volt_t userVoltage6V) {
-  HALSIM_SetRoboRioUserVoltage6V(userVoltage6V.to<double>());
+  HALSIM_SetRoboRioUserVoltage6V(userVoltage6V.value());
 }
 
 std::unique_ptr<CallbackStore> RoboRioSim::RegisterUserCurrent6VCallback(
@@ -95,7 +94,7 @@
 }
 
 void RoboRioSim::SetUserCurrent6V(units::ampere_t userCurrent6V) {
-  HALSIM_SetRoboRioUserCurrent6V(userCurrent6V.to<double>());
+  HALSIM_SetRoboRioUserCurrent6V(userCurrent6V.value());
 }
 
 std::unique_ptr<CallbackStore> RoboRioSim::RegisterUserActive6VCallback(
@@ -107,7 +106,9 @@
   return store;
 }
 
-bool RoboRioSim::GetUserActive6V() { return HALSIM_GetRoboRioUserActive6V(); }
+bool RoboRioSim::GetUserActive6V() {
+  return HALSIM_GetRoboRioUserActive6V();
+}
 
 void RoboRioSim::SetUserActive6V(bool userActive6V) {
   HALSIM_SetRoboRioUserActive6V(userActive6V);
@@ -127,7 +128,7 @@
 }
 
 void RoboRioSim::SetUserVoltage5V(units::volt_t userVoltage5V) {
-  HALSIM_SetRoboRioUserVoltage5V(userVoltage5V.to<double>());
+  HALSIM_SetRoboRioUserVoltage5V(userVoltage5V.value());
 }
 
 std::unique_ptr<CallbackStore> RoboRioSim::RegisterUserCurrent5VCallback(
@@ -144,7 +145,7 @@
 }
 
 void RoboRioSim::SetUserCurrent5V(units::ampere_t userCurrent5V) {
-  HALSIM_SetRoboRioUserCurrent5V(userCurrent5V.to<double>());
+  HALSIM_SetRoboRioUserCurrent5V(userCurrent5V.value());
 }
 
 std::unique_ptr<CallbackStore> RoboRioSim::RegisterUserActive5VCallback(
@@ -156,7 +157,9 @@
   return store;
 }
 
-bool RoboRioSim::GetUserActive5V() { return HALSIM_GetRoboRioUserActive5V(); }
+bool RoboRioSim::GetUserActive5V() {
+  return HALSIM_GetRoboRioUserActive5V();
+}
 
 void RoboRioSim::SetUserActive5V(bool userActive5V) {
   HALSIM_SetRoboRioUserActive5V(userActive5V);
@@ -176,7 +179,7 @@
 }
 
 void RoboRioSim::SetUserVoltage3V3(units::volt_t userVoltage3V3) {
-  HALSIM_SetRoboRioUserVoltage3V3(userVoltage3V3.to<double>());
+  HALSIM_SetRoboRioUserVoltage3V3(userVoltage3V3.value());
 }
 
 std::unique_ptr<CallbackStore> RoboRioSim::RegisterUserCurrent3V3Callback(
@@ -193,7 +196,7 @@
 }
 
 void RoboRioSim::SetUserCurrent3V3(units::ampere_t userCurrent3V3) {
-  HALSIM_SetRoboRioUserCurrent3V3(userCurrent3V3.to<double>());
+  HALSIM_SetRoboRioUserCurrent3V3(userCurrent3V3.value());
 }
 
 std::unique_ptr<CallbackStore> RoboRioSim::RegisterUserActive3V3Callback(
@@ -205,7 +208,9 @@
   return store;
 }
 
-bool RoboRioSim::GetUserActive3V3() { return HALSIM_GetRoboRioUserActive3V3(); }
+bool RoboRioSim::GetUserActive3V3() {
+  return HALSIM_GetRoboRioUserActive3V3();
+}
 
 void RoboRioSim::SetUserActive3V3(bool userActive3V3) {
   HALSIM_SetRoboRioUserActive3V3(userActive3V3);
@@ -220,7 +225,9 @@
   return store;
 }
 
-int RoboRioSim::GetUserFaults6V() { return HALSIM_GetRoboRioUserFaults6V(); }
+int RoboRioSim::GetUserFaults6V() {
+  return HALSIM_GetRoboRioUserFaults6V();
+}
 
 void RoboRioSim::SetUserFaults6V(int userFaults6V) {
   HALSIM_SetRoboRioUserFaults6V(userFaults6V);
@@ -235,7 +242,9 @@
   return store;
 }
 
-int RoboRioSim::GetUserFaults5V() { return HALSIM_GetRoboRioUserFaults5V(); }
+int RoboRioSim::GetUserFaults5V() {
+  return HALSIM_GetRoboRioUserFaults5V();
+}
 
 void RoboRioSim::SetUserFaults5V(int userFaults5V) {
   HALSIM_SetRoboRioUserFaults5V(userFaults5V);
@@ -250,10 +259,31 @@
   return store;
 }
 
-int RoboRioSim::GetUserFaults3V3() { return HALSIM_GetRoboRioUserFaults3V3(); }
+int RoboRioSim::GetUserFaults3V3() {
+  return HALSIM_GetRoboRioUserFaults3V3();
+}
 
 void RoboRioSim::SetUserFaults3V3(int userFaults3V3) {
   HALSIM_SetRoboRioUserFaults3V3(userFaults3V3);
 }
 
-void ResetData() { HALSIM_ResetRoboRioData(); }
+std::unique_ptr<CallbackStore> RoboRioSim::RegisterBrownoutVoltageCallback(
+    NotifyCallback callback, bool initialNotify) {
+  auto store = std::make_unique<CallbackStore>(
+      -1, callback, &HALSIM_CancelRoboRioBrownoutVoltageCallback);
+  store->SetUid(HALSIM_RegisterRoboRioBrownoutVoltageCallback(
+      &CallbackStoreThunk, store.get(), initialNotify));
+  return store;
+}
+
+units::volt_t RoboRioSim::GetBrownoutVoltage() {
+  return units::volt_t(HALSIM_GetRoboRioBrownoutVoltage());
+}
+
+void RoboRioSim::SetBrownoutVoltage(units::volt_t vInVoltage) {
+  HALSIM_SetRoboRioBrownoutVoltage(vInVoltage.value());
+}
+
+void RoboRioSim::ResetData() {
+  HALSIM_ResetRoboRioData();
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/SPIAccelerometerSim.cpp b/wpilibc/src/main/native/cpp/simulation/SPIAccelerometerSim.cpp
index b71f99d..9483af3 100644
--- a/wpilibc/src/main/native/cpp/simulation/SPIAccelerometerSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/SPIAccelerometerSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/SPIAccelerometerSim.h"
 
@@ -15,7 +12,9 @@
 using namespace frc;
 using namespace frc::sim;
 
-SPIAccelerometerSim::SPIAccelerometerSim(int index) { m_index = index; }
+SPIAccelerometerSim::SPIAccelerometerSim(int index) {
+  m_index = index;
+}
 
 std::unique_ptr<CallbackStore> SPIAccelerometerSim::RegisterActiveCallback(
     NotifyCallback callback, bool initialNotify) {
diff --git a/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp b/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
index 3d52cdf..34fd1e3 100644
--- a/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/SimDeviceSim.cpp
@@ -1,15 +1,13 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/SimDeviceSim.h"
 
 #include <string>
 #include <vector>
 
+#include <fmt/format.h>
 #include <hal/SimDevice.h>
 #include <hal/simulation/SimDeviceData.h>
 
@@ -19,10 +17,28 @@
 SimDeviceSim::SimDeviceSim(const char* name)
     : m_handle{HALSIM_GetSimDeviceHandle(name)} {}
 
+SimDeviceSim::SimDeviceSim(const char* name, int index) {
+  m_handle =
+      HALSIM_GetSimDeviceHandle(fmt::format("{}[{}]", name, index).c_str());
+}
+
+SimDeviceSim::SimDeviceSim(const char* name, int index, int channel) {
+  m_handle = HALSIM_GetSimDeviceHandle(
+      fmt::format("{}[{},{}]", name, index, channel).c_str());
+}
+
 hal::SimValue SimDeviceSim::GetValue(const char* name) const {
   return HALSIM_GetSimValueHandle(m_handle, name);
 }
 
+hal::SimInt SimDeviceSim::GetInt(const char* name) const {
+  return HALSIM_GetSimValueHandle(m_handle, name);
+}
+
+hal::SimLong SimDeviceSim::GetLong(const char* name) const {
+  return HALSIM_GetSimValueHandle(m_handle, name);
+}
+
 hal::SimDouble SimDeviceSim::GetDouble(const char* name) const {
   return HALSIM_GetSimValueHandle(m_handle, name);
 }
@@ -40,8 +56,12 @@
   const char** options = HALSIM_GetSimValueEnumOptions(val, &numOptions);
   std::vector<std::string> rv;
   rv.reserve(numOptions);
-  for (int32_t i = 0; i < numOptions; ++i) rv.emplace_back(options[i]);
+  for (int32_t i = 0; i < numOptions; ++i) {
+    rv.emplace_back(options[i]);
+  }
   return rv;
 }
 
-void SimDeviceSim::ResetData() { HALSIM_ResetSimDeviceData(); }
+void SimDeviceSim::ResetData() {
+  HALSIM_ResetSimDeviceData();
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/SimHooks.cpp b/wpilibc/src/main/native/cpp/simulation/SimHooks.cpp
index 28c434c..fe1cc65 100644
--- a/wpilibc/src/main/native/cpp/simulation/SimHooks.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/SimHooks.cpp
@@ -1,40 +1,51 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2018-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/SimHooks.h"
 
 #include <hal/simulation/MockHooks.h>
 
-namespace frc {
-namespace sim {
+namespace frc::sim {
 
-void SetRuntimeType(HAL_RuntimeType type) { HALSIM_SetRuntimeType(type); }
+void SetRuntimeType(HAL_RuntimeType type) {
+  HALSIM_SetRuntimeType(type);
+}
 
-void WaitForProgramStart() { HALSIM_WaitForProgramStart(); }
+void WaitForProgramStart() {
+  HALSIM_WaitForProgramStart();
+}
 
-void SetProgramStarted() { HALSIM_SetProgramStarted(); }
+void SetProgramStarted() {
+  HALSIM_SetProgramStarted();
+}
 
-bool GetProgramStarted() { return HALSIM_GetProgramStarted(); }
+bool GetProgramStarted() {
+  return HALSIM_GetProgramStarted();
+}
 
-void RestartTiming() { HALSIM_RestartTiming(); }
+void RestartTiming() {
+  HALSIM_RestartTiming();
+}
 
-void PauseTiming() { HALSIM_PauseTiming(); }
+void PauseTiming() {
+  HALSIM_PauseTiming();
+}
 
-void ResumeTiming() { HALSIM_ResumeTiming(); }
+void ResumeTiming() {
+  HALSIM_ResumeTiming();
+}
 
-bool IsTimingPaused() { return HALSIM_IsTimingPaused(); }
+bool IsTimingPaused() {
+  return HALSIM_IsTimingPaused();
+}
 
 void StepTiming(units::second_t delta) {
-  HALSIM_StepTiming(static_cast<uint64_t>(delta.to<double>() * 1e6));
+  HALSIM_StepTiming(static_cast<uint64_t>(delta.value() * 1e6));
 }
 
 void StepTimingAsync(units::second_t delta) {
-  HALSIM_StepTimingAsync(static_cast<uint64_t>(delta.to<double>() * 1e6));
+  HALSIM_StepTimingAsync(static_cast<uint64_t>(delta.value() * 1e6));
 }
 
-}  // namespace sim
-}  // namespace frc
+}  // namespace frc::sim
diff --git a/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp b/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
index b33a19e..db56434 100644
--- a/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/SingleJointedArmSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/SingleJointedArmSim.h"
 
@@ -12,7 +9,7 @@
 #include <units/voltage.h>
 #include <wpi/MathExtras.h>
 
-#include "frc/system/RungeKutta.h"
+#include "frc/system/NumericalIntegration.h"
 #include "frc/system/plant/LinearSystemId.h"
 
 using namespace frc;
@@ -42,18 +39,24 @@
           gearbox, gearing, armLength, minAngle, maxAngle, mass,
           simulateGravity, measurementStdDevs) {}
 
-bool SingleJointedArmSim::HasHitLowerLimit(
-    const Eigen::Matrix<double, 2, 1>& x) const {
-  return x(0) < m_minAngle.to<double>();
+bool SingleJointedArmSim::WouldHitLowerLimit(units::radian_t armAngle) const {
+  return armAngle < m_minAngle;
 }
 
-bool SingleJointedArmSim::HasHitUpperLimit(
-    const Eigen::Matrix<double, 2, 1>& x) const {
-  return x(0) > m_maxAngle.to<double>();
+bool SingleJointedArmSim::WouldHitUpperLimit(units::radian_t armAngle) const {
+  return armAngle > m_maxAngle;
+}
+
+bool SingleJointedArmSim::HasHitLowerLimit() const {
+  return WouldHitLowerLimit(units::radian_t(m_y(0)));
+}
+
+bool SingleJointedArmSim::HasHitUpperLimit() const {
+  return WouldHitUpperLimit(units::radian_t(m_y(0)));
 }
 
 units::radian_t SingleJointedArmSim::GetAngle() const {
-  return units::radian_t{m_x(0)};
+  return units::radian_t{m_y(0)};
 }
 
 units::radians_per_second_t SingleJointedArmSim::GetVelocity() const {
@@ -69,12 +72,12 @@
 }
 
 void SingleJointedArmSim::SetInputVoltage(units::volt_t voltage) {
-  SetInput(frc::MakeMatrix<1, 1>(voltage.to<double>()));
+  SetInput(Eigen::Vector<double, 1>{voltage.value()});
 }
 
-Eigen::Matrix<double, 2, 1> SingleJointedArmSim::UpdateX(
-    const Eigen::Matrix<double, 2, 1>& currentXhat,
-    const Eigen::Matrix<double, 1, 1>& u, units::second_t dt) {
+Eigen::Vector<double, 2> SingleJointedArmSim::UpdateX(
+    const Eigen::Vector<double, 2>& currentXhat,
+    const Eigen::Vector<double, 1>& u, units::second_t dt) {
   // Horizontal case:
   // Torque = F * r = I * alpha
   // alpha = F * r / I
@@ -85,24 +88,25 @@
   // We therefore find that f(x, u) = Ax + Bu + [[0] [m * g * r / I *
   // std::cos(theta)]]
 
-  auto updatedXhat = RungeKutta(
-      [&](const auto& x, const auto& u) -> Eigen::Matrix<double, 2, 1> {
-        Eigen::Matrix<double, 2, 1> xdot = m_plant.A() * x + m_plant.B() * u;
+  Eigen::Vector<double, 2> updatedXhat = RKDP(
+      [&](const auto& x, const auto& u) -> Eigen::Vector<double, 2> {
+        Eigen::Vector<double, 2> xdot = m_plant.A() * x + m_plant.B() * u;
 
         if (m_simulateGravity) {
-          xdot += MakeMatrix<2, 1>(0.0, (m_mass * m_r * -9.8 * 3.0 /
-                                         (m_mass * m_r * m_r) * std::cos(x(0)))
-                                            .template to<double>());
+          xdot += Eigen::Vector<double, 2>{
+              0.0, (m_mass * m_r * -9.8 * 3.0 / (m_mass * m_r * m_r) *
+                    std::cos(x(0)))
+                       .value()};
         }
         return xdot;
       },
       currentXhat, u, dt);
 
   // Check for collisions.
-  if (HasHitLowerLimit(updatedXhat)) {
-    return MakeMatrix<2, 1>(m_minAngle.to<double>(), 0.0);
-  } else if (HasHitUpperLimit(updatedXhat)) {
-    return MakeMatrix<2, 1>(m_maxAngle.to<double>(), 0.0);
+  if (WouldHitLowerLimit(units::radian_t(updatedXhat(0)))) {
+    return Eigen::Vector<double, 2>{m_minAngle.value(), 0.0};
+  } else if (WouldHitUpperLimit(units::radian_t(updatedXhat(0)))) {
+    return Eigen::Vector<double, 2>{m_maxAngle.value(), 0.0};
   }
   return updatedXhat;
 }
diff --git a/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp b/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp
new file mode 100644
index 0000000..d2d1687
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/simulation/UltrasonicSim.cpp
@@ -0,0 +1,24 @@
+// 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.
+
+#include "frc/simulation/UltrasonicSim.h"
+
+#include "frc/Ultrasonic.h"
+#include "frc/simulation/SimDeviceSim.h"
+
+using namespace frc::sim;
+
+UltrasonicSim::UltrasonicSim(const frc::Ultrasonic& ultrasonic) {
+  frc::sim::SimDeviceSim deviceSim{"Ultrasonic", ultrasonic.GetEchoChannel()};
+  m_simRangeValid = deviceSim.GetBoolean("Range Valid");
+  m_simRange = deviceSim.GetDouble("Range (in)");
+}
+
+void UltrasonicSim::SetRangeValid(bool isValid) {
+  m_simRangeValid.Set(isValid);
+}
+
+void UltrasonicSim::SetRange(units::meter_t range) {
+  m_simRange.Set(range.value());
+}
diff --git a/wpilibc/src/main/native/cpp/simulation/XboxControllerSim.cpp b/wpilibc/src/main/native/cpp/simulation/XboxControllerSim.cpp
index e3bbdce..393c41a 100644
--- a/wpilibc/src/main/native/cpp/simulation/XboxControllerSim.cpp
+++ b/wpilibc/src/main/native/cpp/simulation/XboxControllerSim.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2020 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/simulation/XboxControllerSim.h"
 
@@ -23,68 +20,66 @@
   SetButtonCount(10);
 }
 
-void XboxControllerSim::SetX(GenericHID::JoystickHand hand, double value) {
-  if (hand == GenericHID::kLeftHand) {
-    SetRawAxis(static_cast<int>(XboxController::Axis::kLeftX), value);
-  } else {
-    SetRawAxis(static_cast<int>(XboxController::Axis::kRightX), value);
-  }
+void XboxControllerSim::SetLeftX(double value) {
+  SetRawAxis(XboxController::Axis::kLeftX, value);
 }
 
-void XboxControllerSim::SetY(GenericHID::JoystickHand hand, double value) {
-  if (hand == GenericHID::kLeftHand) {
-    SetRawAxis(static_cast<int>(XboxController::Axis::kLeftY), value);
-  } else {
-    SetRawAxis(static_cast<int>(XboxController::Axis::kRightY), value);
-  }
+void XboxControllerSim::SetRightX(double value) {
+  SetRawAxis(XboxController::Axis::kRightX, value);
 }
 
-void XboxControllerSim::SetTriggerAxis(GenericHID::JoystickHand hand,
-                                       double value) {
-  if (hand == GenericHID::kLeftHand) {
-    SetRawAxis(static_cast<int>(XboxController::Axis::kLeftTrigger), value);
-  } else {
-    SetRawAxis(static_cast<int>(XboxController::Axis::kRightTrigger), value);
-  }
+void XboxControllerSim::SetLeftY(double value) {
+  SetRawAxis(XboxController::Axis::kLeftY, value);
 }
 
-void XboxControllerSim::SetBumper(GenericHID::JoystickHand hand, bool state) {
-  if (hand == GenericHID::kLeftHand) {
-    SetRawButton(static_cast<int>(XboxController::Button::kBumperLeft), state);
-  } else {
-    SetRawButton(static_cast<int>(XboxController::Button::kBumperRight), state);
-  }
+void XboxControllerSim::SetRightY(double value) {
+  SetRawAxis(XboxController::Axis::kRightY, value);
 }
 
-void XboxControllerSim::SetStickButton(GenericHID::JoystickHand hand,
-                                       bool state) {
-  if (hand == GenericHID::kLeftHand) {
-    SetRawButton(static_cast<int>(XboxController::Button::kStickLeft), state);
-  } else {
-    SetRawButton(static_cast<int>(XboxController::Button::kStickRight), state);
-  }
+void XboxControllerSim::SetLeftTriggerAxis(double value) {
+  SetRawAxis(XboxController::Axis::kLeftTrigger, value);
+}
+
+void XboxControllerSim::SetRightTriggerAxis(double value) {
+  SetRawAxis(XboxController::Axis::kRightTrigger, value);
+}
+
+void XboxControllerSim::SetLeftBumper(bool state) {
+  SetRawButton(XboxController::Button::kLeftBumper, state);
+}
+
+void XboxControllerSim::SetRightBumper(bool state) {
+  SetRawButton(XboxController::Button::kRightBumper, state);
+}
+
+void XboxControllerSim::SetLeftStickButton(bool state) {
+  SetRawButton(XboxController::Button::kLeftStick, state);
+}
+
+void XboxControllerSim::SetRightStickButton(bool state) {
+  SetRawButton(XboxController::Button::kRightStick, state);
 }
 
 void XboxControllerSim::SetAButton(bool state) {
-  SetRawButton(static_cast<int>(XboxController::Button::kA), state);
+  SetRawButton(XboxController::Button::kA, state);
 }
 
 void XboxControllerSim::SetBButton(bool state) {
-  SetRawButton(static_cast<int>(XboxController::Button::kB), state);
+  SetRawButton(XboxController::Button::kB, state);
 }
 
 void XboxControllerSim::SetXButton(bool state) {
-  SetRawButton(static_cast<int>(XboxController::Button::kX), state);
+  SetRawButton(XboxController::Button::kX, state);
 }
 
 void XboxControllerSim::SetYButton(bool state) {
-  SetRawButton(static_cast<int>(XboxController::Button::kY), state);
+  SetRawButton(XboxController::Button::kY, state);
 }
 
 void XboxControllerSim::SetBackButton(bool state) {
-  SetRawButton(static_cast<int>(XboxController::Button::kBack), state);
+  SetRawButton(XboxController::Button::kBack, state);
 }
 
 void XboxControllerSim::SetStartButton(bool state) {
-  SetRawButton(static_cast<int>(XboxController::Button::kStart), state);
+  SetRawButton(XboxController::Button::kStart, state);
 }
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/Field2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/Field2d.cpp
new file mode 100644
index 0000000..2915a12
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/smartdashboard/Field2d.cpp
@@ -0,0 +1,80 @@
+// 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.
+
+#include "frc/smartdashboard/Field2d.h"
+
+#include <networktables/NTSendableBuilder.h>
+#include <wpi/sendable/SendableRegistry.h>
+
+using namespace frc;
+
+Field2d::Field2d() {
+  m_objects.emplace_back(
+      std::make_unique<FieldObject2d>("Robot", FieldObject2d::private_init{}));
+  m_objects[0]->SetPose(Pose2d{});
+  wpi::SendableRegistry::Add(this, "Field");
+}
+
+Field2d::Field2d(Field2d&& rhs) : SendableHelper(std::move(rhs)) {
+  std::swap(m_table, rhs.m_table);
+  std::swap(m_objects, rhs.m_objects);
+}
+
+Field2d& Field2d::operator=(Field2d&& rhs) {
+  SendableHelper::operator=(std::move(rhs));
+
+  std::swap(m_table, rhs.m_table);
+  std::swap(m_objects, rhs.m_objects);
+
+  return *this;
+}
+
+void Field2d::SetRobotPose(const Pose2d& pose) {
+  std::scoped_lock lock(m_mutex);
+  m_objects[0]->SetPose(pose);
+}
+
+void Field2d::SetRobotPose(units::meter_t x, units::meter_t y,
+                           Rotation2d rotation) {
+  std::scoped_lock lock(m_mutex);
+  m_objects[0]->SetPose(x, y, rotation);
+}
+
+Pose2d Field2d::GetRobotPose() const {
+  std::scoped_lock lock(m_mutex);
+  return m_objects[0]->GetPose();
+}
+
+FieldObject2d* Field2d::GetObject(std::string_view name) {
+  std::scoped_lock lock(m_mutex);
+  for (auto&& obj : m_objects) {
+    if (obj->m_name == name) {
+      return obj.get();
+    }
+  }
+  m_objects.emplace_back(
+      std::make_unique<FieldObject2d>(name, FieldObject2d::private_init{}));
+  auto obj = m_objects.back().get();
+  if (m_table) {
+    obj->m_entry = m_table->GetEntry(obj->m_name);
+  }
+  return obj;
+}
+
+FieldObject2d* Field2d::GetRobotObject() {
+  std::scoped_lock lock(m_mutex);
+  return m_objects[0].get();
+}
+
+void Field2d::InitSendable(nt::NTSendableBuilder& builder) {
+  builder.SetSmartDashboardType("Field2d");
+
+  std::scoped_lock lock(m_mutex);
+  m_table = builder.GetTable();
+  for (auto&& obj : m_objects) {
+    std::scoped_lock lock2(obj->m_mutex);
+    obj->m_entry = m_table->GetEntry(obj->m_name);
+    obj->UpdateEntry(true);
+  }
+}
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/FieldObject2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/FieldObject2d.cpp
new file mode 100644
index 0000000..93d6d7e
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/smartdashboard/FieldObject2d.cpp
@@ -0,0 +1,170 @@
+// 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.
+
+#include "frc/smartdashboard/FieldObject2d.h"
+
+#include <vector>
+
+#include <wpi/Endian.h>
+#include <wpi/MathExtras.h>
+
+#include "frc/trajectory/Trajectory.h"
+
+using namespace frc;
+
+FieldObject2d::FieldObject2d(FieldObject2d&& rhs) {
+  std::swap(m_name, rhs.m_name);
+  std::swap(m_entry, rhs.m_entry);
+  std::swap(m_poses, rhs.m_poses);
+}
+
+FieldObject2d& FieldObject2d::operator=(FieldObject2d&& rhs) {
+  std::swap(m_name, rhs.m_name);
+  std::swap(m_entry, rhs.m_entry);
+  std::swap(m_poses, rhs.m_poses);
+
+  return *this;
+}
+
+void FieldObject2d::SetPose(const Pose2d& pose) {
+  SetPoses({pose});
+}
+
+void FieldObject2d::SetPose(units::meter_t x, units::meter_t y,
+                            Rotation2d rotation) {
+  SetPoses({{x, y, rotation}});
+}
+
+Pose2d FieldObject2d::GetPose() const {
+  std::scoped_lock lock(m_mutex);
+  UpdateFromEntry();
+  if (m_poses.empty()) {
+    return {};
+  }
+  return m_poses[0];
+}
+
+void FieldObject2d::SetPoses(wpi::span<const Pose2d> poses) {
+  std::scoped_lock lock(m_mutex);
+  m_poses.assign(poses.begin(), poses.end());
+  UpdateEntry();
+}
+
+void FieldObject2d::SetPoses(std::initializer_list<Pose2d> poses) {
+  SetPoses({poses.begin(), poses.end()});
+}
+
+void FieldObject2d::SetTrajectory(const Trajectory& trajectory) {
+  std::scoped_lock lock(m_mutex);
+  m_poses.clear();
+  m_poses.reserve(trajectory.States().size());
+  for (auto&& state : trajectory.States()) {
+    m_poses.push_back(state.pose);
+  }
+  UpdateEntry();
+}
+
+std::vector<Pose2d> FieldObject2d::GetPoses() const {
+  std::scoped_lock lock(m_mutex);
+  UpdateFromEntry();
+  return std::vector<Pose2d>(m_poses.begin(), m_poses.end());
+}
+
+wpi::span<const Pose2d> FieldObject2d::GetPoses(
+    wpi::SmallVectorImpl<Pose2d>& out) const {
+  std::scoped_lock lock(m_mutex);
+  UpdateFromEntry();
+  out.assign(m_poses.begin(), m_poses.end());
+  return out;
+}
+
+void FieldObject2d::UpdateEntry(bool setDefault) {
+  if (!m_entry) {
+    return;
+  }
+  if (m_poses.size() < (255 / 3)) {
+    wpi::SmallVector<double, 9> arr;
+    for (auto&& pose : m_poses) {
+      auto& translation = pose.Translation();
+      arr.push_back(translation.X().value());
+      arr.push_back(translation.Y().value());
+      arr.push_back(pose.Rotation().Degrees().value());
+    }
+    if (setDefault) {
+      m_entry.SetDefaultDoubleArray(arr);
+    } else {
+      m_entry.ForceSetDoubleArray(arr);
+    }
+  } else {
+    // send as raw array of doubles if too big for NT array
+    std::vector<char> arr;
+    arr.resize(m_poses.size() * 3 * 8);
+    char* p = arr.data();
+    for (auto&& pose : m_poses) {
+      auto& translation = pose.Translation();
+      wpi::support::endian::write64be(
+          p, wpi::DoubleToBits(translation.X().value()));
+      p += 8;
+      wpi::support::endian::write64be(
+          p, wpi::DoubleToBits(translation.Y().value()));
+      p += 8;
+      wpi::support::endian::write64be(
+          p, wpi::DoubleToBits(pose.Rotation().Degrees().value()));
+      p += 8;
+    }
+    if (setDefault) {
+      m_entry.SetDefaultRaw({arr.data(), arr.size()});
+    } else {
+      m_entry.ForceSetRaw({arr.data(), arr.size()});
+    }
+  }
+}
+
+void FieldObject2d::UpdateFromEntry() const {
+  if (!m_entry) {
+    return;
+  }
+  auto val = m_entry.GetValue();
+  if (!val) {
+    return;
+  }
+
+  if (val->IsDoubleArray()) {
+    auto arr = val->GetDoubleArray();
+    auto size = arr.size();
+    if ((size % 3) != 0) {
+      return;
+    }
+    m_poses.resize(size / 3);
+    for (size_t i = 0; i < size / 3; ++i) {
+      m_poses[i] =
+          Pose2d{units::meter_t{arr[i * 3 + 0]}, units::meter_t{arr[i * 3 + 1]},
+                 Rotation2d{units::degree_t{arr[i * 3 + 2]}}};
+    }
+  } else if (val->IsRaw()) {
+    // treat it simply as an array of doubles
+    std::string_view data = val->GetRaw();
+
+    // must be triples of doubles
+    auto size = data.size();
+    if ((size % (3 * 8)) != 0) {
+      return;
+    }
+    m_poses.resize(size / (3 * 8));
+    const char* p = data.data();
+    for (size_t i = 0; i < size / (3 * 8); ++i) {
+      double x = wpi::BitsToDouble(
+          wpi::support::endian::readNext<uint64_t, wpi::support::big,
+                                         wpi::support::unaligned>(p));
+      double y = wpi::BitsToDouble(
+          wpi::support::endian::readNext<uint64_t, wpi::support::big,
+                                         wpi::support::unaligned>(p));
+      double rot = wpi::BitsToDouble(
+          wpi::support::endian::readNext<uint64_t, wpi::support::big,
+                                         wpi::support::unaligned>(p));
+      m_poses[i] = Pose2d{units::meter_t{x}, units::meter_t{y},
+                          Rotation2d{units::degree_t{rot}}};
+    }
+  }
+}
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/ListenerExecutor.cpp b/wpilibc/src/main/native/cpp/smartdashboard/ListenerExecutor.cpp
index 75b373a..2a53196 100644
--- a/wpilibc/src/main/native/cpp/smartdashboard/ListenerExecutor.cpp
+++ b/wpilibc/src/main/native/cpp/smartdashboard/ListenerExecutor.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019 FIRST. All Rights Reserved.                             */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/smartdashboard/ListenerExecutor.h"
 
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp
new file mode 100644
index 0000000..bfa827a
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/smartdashboard/Mechanism2d.cpp
@@ -0,0 +1,55 @@
+// 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.
+
+#include "frc/smartdashboard/Mechanism2d.h"
+
+#include <cstdio>
+
+#include <networktables/NTSendableBuilder.h>
+
+using namespace frc;
+
+static constexpr char kBackgroundColor[] = "backgroundColor";
+static constexpr char kDims[] = "dims";
+
+Mechanism2d::Mechanism2d(double width, double height,
+                         const Color8Bit& backgroundColor)
+    : m_width{width}, m_height{height} {
+  SetBackgroundColor(backgroundColor);
+}
+
+MechanismRoot2d* Mechanism2d::GetRoot(std::string_view name, double x,
+                                      double y) {
+  auto& obj = m_roots[name];
+  if (obj) {
+    return obj.get();
+  }
+  obj = std::make_unique<MechanismRoot2d>(name, x, y,
+                                          MechanismRoot2d::private_init{});
+  if (m_table) {
+    obj->Update(m_table->GetSubTable(name));
+  }
+  return obj.get();
+}
+
+void Mechanism2d::SetBackgroundColor(const Color8Bit& color) {
+  std::snprintf(m_color, sizeof(m_color), "#%02X%02X%02X", color.red,
+                color.green, color.blue);
+  if (m_table) {
+    m_table->GetEntry(kBackgroundColor).SetString(m_color);
+  }
+}
+
+void Mechanism2d::InitSendable(nt::NTSendableBuilder& builder) {
+  builder.SetSmartDashboardType("Mechanism2d");
+
+  std::scoped_lock lock(m_mutex);
+  m_table = builder.GetTable();
+  m_table->GetEntry(kDims).SetDoubleArray({m_width, m_height});
+  m_table->GetEntry(kBackgroundColor).SetString(m_color);
+  for (const auto& entry : m_roots) {
+    const auto& root = entry.getValue().get();
+    root->Update(m_table->GetSubTable(entry.getKey()));
+  }
+}
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp
new file mode 100644
index 0000000..175db0d
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/smartdashboard/MechanismLigament2d.cpp
@@ -0,0 +1,82 @@
+// 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.
+
+#include "frc/smartdashboard/MechanismLigament2d.h"
+
+#include <cstdio>
+
+using namespace frc;
+
+MechanismLigament2d::MechanismLigament2d(std::string_view name, double length,
+                                         units::degree_t angle,
+                                         double lineWeight,
+                                         const frc::Color8Bit& color)
+    : MechanismObject2d(name),
+      m_length{length},
+      m_angle{angle.value()},
+      m_weight{lineWeight} {
+  SetColor(color);
+}
+
+void MechanismLigament2d::UpdateEntries(
+    std::shared_ptr<nt::NetworkTable> table) {
+  std::scoped_lock lock(m_mutex);
+  table->GetEntry(".type").SetString("line");
+
+  m_colorEntry = table->GetEntry("color");
+  m_angleEntry = table->GetEntry("angle");
+  m_weightEntry = table->GetEntry("weight");
+  m_lengthEntry = table->GetEntry("length");
+  Flush();
+}
+
+void MechanismLigament2d::SetColor(const Color8Bit& color) {
+  std::scoped_lock lock(m_mutex);
+  std::snprintf(m_color, sizeof(m_color), "#%02X%02X%02X", color.red,
+                color.green, color.blue);
+  Flush();
+}
+
+void MechanismLigament2d::SetAngle(units::degree_t angle) {
+  std::scoped_lock lock(m_mutex);
+  m_angle = angle.value();
+  Flush();
+}
+
+void MechanismLigament2d::SetLineWeight(double lineWidth) {
+  std::scoped_lock lock(m_mutex);
+  m_weight = lineWidth;
+  Flush();
+}
+
+double MechanismLigament2d::GetAngle() {
+  if (m_angleEntry) {
+    m_angle = m_angleEntry.GetDouble(0.0);
+  }
+  return m_angle;
+}
+
+double MechanismLigament2d::GetLength() {
+  if (m_lengthEntry) {
+    m_length = m_lengthEntry.GetDouble(0.0);
+  }
+  return m_length;
+}
+
+void MechanismLigament2d::SetLength(double length) {
+  std::scoped_lock lock(m_mutex);
+  m_length = length;
+  Flush();
+}
+
+#define SAFE_WRITE(data, Type)           \
+  if (m_##data##Entry) {                 \
+    m_##data##Entry.Set##Type(m_##data); \
+  }
+void MechanismLigament2d::Flush() {
+  SAFE_WRITE(color, String)
+  SAFE_WRITE(angle, Double)
+  SAFE_WRITE(length, Double)
+  SAFE_WRITE(weight, Double)
+}
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/MechanismObject2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/MechanismObject2d.cpp
new file mode 100644
index 0000000..15d24a3
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/smartdashboard/MechanismObject2d.cpp
@@ -0,0 +1,23 @@
+// 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.
+
+#include "frc/smartdashboard/MechanismObject2d.h"
+
+using namespace frc;
+
+MechanismObject2d::MechanismObject2d(std::string_view name) : m_name{name} {}
+
+const std::string& MechanismObject2d::GetName() const {
+  return m_name;
+}
+
+void MechanismObject2d::Update(std::shared_ptr<nt::NetworkTable> table) {
+  std::scoped_lock lock(m_mutex);
+  m_table = table;
+  UpdateEntries(m_table);
+  for (const wpi::StringMapEntry<std::unique_ptr<MechanismObject2d>>& entry :
+       m_objects) {
+    entry.getValue()->Update(m_table->GetSubTable(entry.getKey()));
+  }
+}
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/MechanismRoot2d.cpp b/wpilibc/src/main/native/cpp/smartdashboard/MechanismRoot2d.cpp
new file mode 100644
index 0000000..07d15f0
--- /dev/null
+++ b/wpilibc/src/main/native/cpp/smartdashboard/MechanismRoot2d.cpp
@@ -0,0 +1,37 @@
+// 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.
+
+#include "frc/smartdashboard/MechanismRoot2d.h"
+
+#include "frc/util/Color8Bit.h"
+
+using namespace frc;
+
+MechanismRoot2d::MechanismRoot2d(std::string_view name, double x, double y,
+                                 const private_init&)
+    : MechanismObject2d(name), m_x{x}, m_y{y} {}
+
+void MechanismRoot2d::SetPosition(double x, double y) {
+  std::scoped_lock lock(m_mutex);
+  m_x = x;
+  m_y = y;
+  Flush();
+}
+
+void MechanismRoot2d::UpdateEntries(std::shared_ptr<nt::NetworkTable> table) {
+  std::scoped_lock lock(m_mutex);
+  m_xEntry = table->GetEntry("x");
+  m_yEntry = table->GetEntry("y");
+  Flush();
+}
+
+inline void MechanismRoot2d::Flush() {
+  std::scoped_lock lock(m_mutex);
+  if (m_xEntry) {
+    m_xEntry.SetDouble(m_x);
+  }
+  if (m_yEntry) {
+    m_yEntry.SetDouble(m_y);
+  }
+}
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableBase.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableBase.cpp
deleted file mode 100644
index 7fc8635..0000000
--- a/wpilibc/src/main/native/cpp/smartdashboard/SendableBase.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/smartdashboard/SendableBase.h"
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-using namespace frc;
-
-SendableBase::SendableBase(bool addLiveWindow) {
-  if (addLiveWindow)
-    SendableRegistry::GetInstance().AddLW(this, "");
-  else
-    SendableRegistry::GetInstance().Add(this, "");
-}
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
index 135f4be..48af198 100644
--- a/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
+++ b/wpilibc/src/main/native/cpp/smartdashboard/SendableBuilderImpl.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/smartdashboard/SendableBuilderImpl.h"
 
@@ -23,14 +20,20 @@
   return m_table;
 }
 
-bool SendableBuilderImpl::HasTable() const { return m_table != nullptr; }
+bool SendableBuilderImpl::IsPublished() const {
+  return m_table != nullptr;
+}
 
-bool SendableBuilderImpl::IsActuator() const { return m_actuator; }
+bool SendableBuilderImpl::IsActuator() const {
+  return m_actuator;
+}
 
-void SendableBuilderImpl::UpdateTable() {
+void SendableBuilderImpl::Update() {
   uint64_t time = nt::Now();
   for (auto& property : m_properties) {
-    if (property.update) property.update(property.entry, time);
+    if (property.update) {
+      property.update(property.entry, time);
+    }
   }
   for (auto& updateTable : m_updateTables) {
     updateTable();
@@ -38,28 +41,42 @@
 }
 
 void SendableBuilderImpl::StartListeners() {
-  for (auto& property : m_properties) property.StartListener();
-  if (m_controllableEntry) m_controllableEntry.SetBoolean(true);
+  for (auto& property : m_properties) {
+    property.StartListener();
+  }
+  if (m_controllableEntry) {
+    m_controllableEntry.SetBoolean(true);
+  }
 }
 
 void SendableBuilderImpl::StopListeners() {
-  for (auto& property : m_properties) property.StopListener();
-  if (m_controllableEntry) m_controllableEntry.SetBoolean(false);
+  for (auto& property : m_properties) {
+    property.StopListener();
+  }
+  if (m_controllableEntry) {
+    m_controllableEntry.SetBoolean(false);
+  }
 }
 
 void SendableBuilderImpl::StartLiveWindowMode() {
-  if (m_safeState) m_safeState();
+  if (m_safeState) {
+    m_safeState();
+  }
   StartListeners();
 }
 
 void SendableBuilderImpl::StopLiveWindowMode() {
   StopListeners();
-  if (m_safeState) m_safeState();
+  if (m_safeState) {
+    m_safeState();
+  }
 }
 
-void SendableBuilderImpl::ClearProperties() { m_properties.clear(); }
+void SendableBuilderImpl::ClearProperties() {
+  m_properties.clear();
+}
 
-void SendableBuilderImpl::SetSmartDashboardType(const wpi::Twine& type) {
+void SendableBuilderImpl::SetSmartDashboardType(std::string_view type) {
   m_table->GetEntry(".type").SetString(type);
 }
 
@@ -76,11 +93,11 @@
   m_updateTables.emplace_back(std::move(func));
 }
 
-nt::NetworkTableEntry SendableBuilderImpl::GetEntry(const wpi::Twine& key) {
+nt::NetworkTableEntry SendableBuilderImpl::GetEntry(std::string_view key) {
   return m_table->GetEntry(key);
 }
 
-void SendableBuilderImpl::AddBooleanProperty(const wpi::Twine& key,
+void SendableBuilderImpl::AddBooleanProperty(std::string_view key,
                                              std::function<bool()> getter,
                                              std::function<void(bool)> setter) {
   m_properties.emplace_back(*m_table, key);
@@ -95,7 +112,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsBoolean()) return;
+            if (!event.value->IsBoolean()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetBoolean()); });
           },
@@ -105,7 +124,7 @@
 }
 
 void SendableBuilderImpl::AddDoubleProperty(
-    const wpi::Twine& key, std::function<double()> getter,
+    std::string_view key, std::function<double()> getter,
     std::function<void(double)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
@@ -119,7 +138,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsDouble()) return;
+            if (!event.value->IsDouble()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetDouble()); });
           },
@@ -129,8 +150,8 @@
 }
 
 void SendableBuilderImpl::AddStringProperty(
-    const wpi::Twine& key, std::function<std::string()> getter,
-    std::function<void(wpi::StringRef)> setter) {
+    std::string_view key, std::function<std::string()> getter,
+    std::function<void(std::string_view)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -143,7 +164,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsString()) return;
+            if (!event.value->IsString()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetString()); });
           },
@@ -153,8 +176,8 @@
 }
 
 void SendableBuilderImpl::AddBooleanArrayProperty(
-    const wpi::Twine& key, std::function<std::vector<int>()> getter,
-    std::function<void(wpi::ArrayRef<int>)> setter) {
+    std::string_view key, std::function<std::vector<int>()> getter,
+    std::function<void(wpi::span<const int>)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -167,7 +190,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsBooleanArray()) return;
+            if (!event.value->IsBooleanArray()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetBooleanArray()); });
           },
@@ -177,8 +202,8 @@
 }
 
 void SendableBuilderImpl::AddDoubleArrayProperty(
-    const wpi::Twine& key, std::function<std::vector<double>()> getter,
-    std::function<void(wpi::ArrayRef<double>)> setter) {
+    std::string_view key, std::function<std::vector<double>()> getter,
+    std::function<void(wpi::span<const double>)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -191,7 +216,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsDoubleArray()) return;
+            if (!event.value->IsDoubleArray()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetDoubleArray()); });
           },
@@ -201,8 +228,8 @@
 }
 
 void SendableBuilderImpl::AddStringArrayProperty(
-    const wpi::Twine& key, std::function<std::vector<std::string>()> getter,
-    std::function<void(wpi::ArrayRef<std::string>)> setter) {
+    std::string_view key, std::function<std::vector<std::string>()> getter,
+    std::function<void(wpi::span<const std::string>)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -215,7 +242,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsStringArray()) return;
+            if (!event.value->IsStringArray()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetStringArray()); });
           },
@@ -225,8 +254,8 @@
 }
 
 void SendableBuilderImpl::AddRawProperty(
-    const wpi::Twine& key, std::function<std::string()> getter,
-    std::function<void(wpi::StringRef)> setter) {
+    std::string_view key, std::function<std::string()> getter,
+    std::function<void(std::string_view)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -239,7 +268,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsRaw()) return;
+            if (!event.value->IsRaw()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetRaw()); });
           },
@@ -249,7 +280,7 @@
 }
 
 void SendableBuilderImpl::AddValueProperty(
-    const wpi::Twine& key, std::function<std::shared_ptr<nt::Value>()> getter,
+    std::string_view key, std::function<std::shared_ptr<nt::Value>()> getter,
     std::function<void(std::shared_ptr<nt::Value>)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
@@ -271,9 +302,9 @@
 }
 
 void SendableBuilderImpl::AddSmallStringProperty(
-    const wpi::Twine& key,
-    std::function<wpi::StringRef(wpi::SmallVectorImpl<char>& buf)> getter,
-    std::function<void(wpi::StringRef)> setter) {
+    std::string_view key,
+    std::function<std::string_view(wpi::SmallVectorImpl<char>& buf)> getter,
+    std::function<void(std::string_view)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -287,7 +318,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsString()) return;
+            if (!event.value->IsString()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetString()); });
           },
@@ -297,9 +330,9 @@
 }
 
 void SendableBuilderImpl::AddSmallBooleanArrayProperty(
-    const wpi::Twine& key,
-    std::function<wpi::ArrayRef<int>(wpi::SmallVectorImpl<int>& buf)> getter,
-    std::function<void(wpi::ArrayRef<int>)> setter) {
+    std::string_view key,
+    std::function<wpi::span<const int>(wpi::SmallVectorImpl<int>& buf)> getter,
+    std::function<void(wpi::span<const int>)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -313,7 +346,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsBooleanArray()) return;
+            if (!event.value->IsBooleanArray()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetBooleanArray()); });
           },
@@ -323,10 +358,10 @@
 }
 
 void SendableBuilderImpl::AddSmallDoubleArrayProperty(
-    const wpi::Twine& key,
-    std::function<wpi::ArrayRef<double>(wpi::SmallVectorImpl<double>& buf)>
+    std::string_view key,
+    std::function<wpi::span<const double>(wpi::SmallVectorImpl<double>& buf)>
         getter,
-    std::function<void(wpi::ArrayRef<double>)> setter) {
+    std::function<void(wpi::span<const double>)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -340,7 +375,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsDoubleArray()) return;
+            if (!event.value->IsDoubleArray()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetDoubleArray()); });
           },
@@ -350,11 +387,11 @@
 }
 
 void SendableBuilderImpl::AddSmallStringArrayProperty(
-    const wpi::Twine& key,
+    std::string_view key,
     std::function<
-        wpi::ArrayRef<std::string>(wpi::SmallVectorImpl<std::string>& buf)>
+        wpi::span<const std::string>(wpi::SmallVectorImpl<std::string>& buf)>
         getter,
-    std::function<void(wpi::ArrayRef<std::string>)> setter) {
+    std::function<void(wpi::span<const std::string>)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -368,7 +405,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsStringArray()) return;
+            if (!event.value->IsStringArray()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetStringArray()); });
           },
@@ -378,9 +417,9 @@
 }
 
 void SendableBuilderImpl::AddSmallRawProperty(
-    const wpi::Twine& key,
-    std::function<wpi::StringRef(wpi::SmallVectorImpl<char>& buf)> getter,
-    std::function<void(wpi::StringRef)> setter) {
+    std::string_view key,
+    std::function<std::string_view(wpi::SmallVectorImpl<char>& buf)> getter,
+    std::function<void(std::string_view)> setter) {
   m_properties.emplace_back(*m_table, key);
   if (getter) {
     m_properties.back().update = [=](nt::NetworkTableEntry entry,
@@ -394,7 +433,9 @@
         [=](nt::NetworkTableEntry entry) -> NT_EntryListener {
       return entry.AddListener(
           [=](const nt::EntryNotification& event) {
-            if (!event.value->IsRaw()) return;
+            if (!event.value->IsRaw()) {
+              return;
+            }
             SmartDashboard::PostListenerTask(
                 [=] { setter(event.value->GetRaw()); });
           },
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
index 2f7af9c..550d40c 100644
--- a/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
+++ b/wpilibc/src/main/native/cpp/smartdashboard/SendableChooserBase.cpp
@@ -1,20 +1,17 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2017-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/smartdashboard/SendableChooserBase.h"
 
-#include "frc/smartdashboard/SendableRegistry.h"
+#include <wpi/sendable/SendableRegistry.h>
 
 using namespace frc;
 
 std::atomic_int SendableChooserBase::s_instances{0};
 
 SendableChooserBase::SendableChooserBase() : m_instance{s_instances++} {
-  SendableRegistry::GetInstance().Add(this, "SendableChooser", m_instance);
+  wpi::SendableRegistry::Add(this, "SendableChooser", m_instance);
 }
 
 SendableChooserBase::SendableChooserBase(SendableChooserBase&& oth)
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SendableRegistry.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SendableRegistry.cpp
deleted file mode 100644
index 4480313..0000000
--- a/wpilibc/src/main/native/cpp/smartdashboard/SendableRegistry.cpp
+++ /dev/null
@@ -1,372 +0,0 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2019-2020 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
-
-#include "frc/smartdashboard/SendableRegistry.h"
-
-#include <memory>
-
-#include <wpi/DenseMap.h>
-#include <wpi/SmallVector.h>
-#include <wpi/UidVector.h>
-#include <wpi/mutex.h>
-
-#include "frc/smartdashboard/Sendable.h"
-#include "frc/smartdashboard/SendableBuilderImpl.h"
-
-using namespace frc;
-
-struct SendableRegistry::Impl {
-  struct Component {
-    Sendable* sendable = nullptr;
-    SendableBuilderImpl builder;
-    std::string name;
-    std::string subsystem = "Ungrouped";
-    Sendable* parent = nullptr;
-    bool liveWindow = false;
-    wpi::SmallVector<std::shared_ptr<void>, 2> data;
-
-    void SetName(const wpi::Twine& moduleType, int channel) {
-      name =
-          (moduleType + wpi::Twine('[') + wpi::Twine(channel) + wpi::Twine(']'))
-              .str();
-    }
-
-    void SetName(const wpi::Twine& moduleType, int moduleNumber, int channel) {
-      name = (moduleType + wpi::Twine('[') + wpi::Twine(moduleNumber) +
-              wpi::Twine(',') + wpi::Twine(channel) + wpi::Twine(']'))
-                 .str();
-    }
-  };
-
-  wpi::recursive_mutex mutex;
-
-  wpi::UidVector<std::unique_ptr<Component>, 32> components;
-  wpi::DenseMap<void*, UID> componentMap;
-  int nextDataHandle = 0;
-
-  Component& GetOrAdd(void* sendable, UID* uid = nullptr);
-};
-
-SendableRegistry::Impl::Component& SendableRegistry::Impl::GetOrAdd(
-    void* sendable, UID* uid) {
-  UID& compUid = componentMap[sendable];
-  if (compUid == 0)
-    compUid = components.emplace_back(std::make_unique<Component>()) + 1;
-  if (uid) *uid = compUid;
-
-  return *components[compUid - 1];
-}
-
-SendableRegistry& SendableRegistry::GetInstance() {
-  static SendableRegistry instance;
-  return instance;
-}
-
-void SendableRegistry::Add(Sendable* sendable, const wpi::Twine& name) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(sendable);
-  comp.sendable = sendable;
-  comp.name = name.str();
-}
-
-void SendableRegistry::Add(Sendable* sendable, const wpi::Twine& moduleType,
-                           int channel) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(sendable);
-  comp.sendable = sendable;
-  comp.SetName(moduleType, channel);
-}
-
-void SendableRegistry::Add(Sendable* sendable, const wpi::Twine& moduleType,
-                           int moduleNumber, int channel) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(sendable);
-  comp.sendable = sendable;
-  comp.SetName(moduleType, moduleNumber, channel);
-}
-
-void SendableRegistry::Add(Sendable* sendable, const wpi::Twine& subsystem,
-                           const wpi::Twine& name) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(sendable);
-  comp.sendable = sendable;
-  comp.name = name.str();
-  comp.subsystem = subsystem.str();
-}
-
-void SendableRegistry::AddLW(Sendable* sendable, const wpi::Twine& name) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(sendable);
-  comp.sendable = sendable;
-  comp.liveWindow = true;
-  comp.name = name.str();
-}
-
-void SendableRegistry::AddLW(Sendable* sendable, const wpi::Twine& moduleType,
-                             int channel) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(sendable);
-  comp.sendable = sendable;
-  comp.liveWindow = true;
-  comp.SetName(moduleType, channel);
-}
-
-void SendableRegistry::AddLW(Sendable* sendable, const wpi::Twine& moduleType,
-                             int moduleNumber, int channel) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(sendable);
-  comp.sendable = sendable;
-  comp.liveWindow = true;
-  comp.SetName(moduleType, moduleNumber, channel);
-}
-
-void SendableRegistry::AddLW(Sendable* sendable, const wpi::Twine& subsystem,
-                             const wpi::Twine& name) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(sendable);
-  comp.sendable = sendable;
-  comp.liveWindow = true;
-  comp.name = name.str();
-  comp.subsystem = subsystem.str();
-}
-
-void SendableRegistry::AddChild(Sendable* parent, Sendable* child) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(child);
-  comp.parent = parent;
-}
-
-void SendableRegistry::AddChild(Sendable* parent, void* child) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto& comp = m_impl->GetOrAdd(child);
-  comp.parent = parent;
-}
-
-bool SendableRegistry::Remove(Sendable* sendable) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end()) return false;
-  UID compUid = it->getSecond();
-  m_impl->components.erase(compUid - 1);
-  m_impl->componentMap.erase(it);
-  // update any parent pointers
-  for (auto&& comp : m_impl->components) {
-    if (comp->parent == sendable) comp->parent = nullptr;
-  }
-  return true;
-}
-
-void SendableRegistry::Move(Sendable* to, Sendable* from) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(from);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return;
-  UID compUid = it->getSecond();
-  m_impl->componentMap.erase(it);
-  m_impl->componentMap[to] = compUid;
-  auto& comp = *m_impl->components[compUid - 1];
-  comp.sendable = to;
-  if (comp.builder.HasTable()) {
-    // rebuild builder, as lambda captures can point to "from"
-    comp.builder.ClearProperties();
-    to->InitSendable(comp.builder);
-  }
-  // update any parent pointers
-  for (auto&& comp : m_impl->components) {
-    if (comp->parent == from) comp->parent = to;
-  }
-}
-
-bool SendableRegistry::Contains(const Sendable* sendable) const {
-  std::scoped_lock lock(m_impl->mutex);
-  return m_impl->componentMap.count(sendable) != 0;
-}
-
-std::string SendableRegistry::GetName(const Sendable* sendable) const {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return std::string{};
-  return m_impl->components[it->getSecond() - 1]->name;
-}
-
-void SendableRegistry::SetName(Sendable* sendable, const wpi::Twine& name) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return;
-  m_impl->components[it->getSecond() - 1]->name = name.str();
-}
-
-void SendableRegistry::SetName(Sendable* sendable, const wpi::Twine& moduleType,
-                               int channel) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return;
-  m_impl->components[it->getSecond() - 1]->SetName(moduleType, channel);
-}
-
-void SendableRegistry::SetName(Sendable* sendable, const wpi::Twine& moduleType,
-                               int moduleNumber, int channel) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return;
-  m_impl->components[it->getSecond() - 1]->SetName(moduleType, moduleNumber,
-                                                   channel);
-}
-
-void SendableRegistry::SetName(Sendable* sendable, const wpi::Twine& subsystem,
-                               const wpi::Twine& name) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return;
-  auto& comp = *m_impl->components[it->getSecond() - 1];
-  comp.name = name.str();
-  comp.subsystem = subsystem.str();
-}
-
-std::string SendableRegistry::GetSubsystem(const Sendable* sendable) const {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return std::string{};
-  return m_impl->components[it->getSecond() - 1]->subsystem;
-}
-
-void SendableRegistry::SetSubsystem(Sendable* sendable,
-                                    const wpi::Twine& subsystem) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return;
-  m_impl->components[it->getSecond() - 1]->subsystem = subsystem.str();
-}
-
-int SendableRegistry::GetDataHandle() {
-  std::scoped_lock lock(m_impl->mutex);
-  return m_impl->nextDataHandle++;
-}
-
-std::shared_ptr<void> SendableRegistry::SetData(Sendable* sendable, int handle,
-                                                std::shared_ptr<void> data) {
-  assert(handle >= 0);
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return nullptr;
-  auto& comp = *m_impl->components[it->getSecond() - 1];
-  std::shared_ptr<void> rv;
-  if (static_cast<size_t>(handle) < comp.data.size())
-    rv = std::move(comp.data[handle]);
-  else
-    comp.data.resize(handle + 1);
-  comp.data[handle] = std::move(data);
-  return rv;
-}
-
-std::shared_ptr<void> SendableRegistry::GetData(Sendable* sendable,
-                                                int handle) {
-  assert(handle >= 0);
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return nullptr;
-  auto& comp = *m_impl->components[it->getSecond() - 1];
-  if (static_cast<size_t>(handle) >= comp.data.size()) return nullptr;
-  return comp.data[handle];
-}
-
-void SendableRegistry::EnableLiveWindow(Sendable* sendable) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return;
-  m_impl->components[it->getSecond() - 1]->liveWindow = true;
-}
-
-void SendableRegistry::DisableLiveWindow(Sendable* sendable) {
-  std::scoped_lock lock(m_impl->mutex);
-  auto it = m_impl->componentMap.find(sendable);
-  if (it == m_impl->componentMap.end() ||
-      !m_impl->components[it->getSecond() - 1])
-    return;
-  m_impl->components[it->getSecond() - 1]->liveWindow = false;
-}
-
-SendableRegistry::UID SendableRegistry::GetUniqueId(Sendable* sendable) {
-  std::scoped_lock lock(m_impl->mutex);
-  UID uid;
-  auto& comp = m_impl->GetOrAdd(sendable, &uid);
-  comp.sendable = sendable;
-  return uid;
-}
-
-Sendable* SendableRegistry::GetSendable(UID uid) {
-  if (uid == 0) return nullptr;
-  std::scoped_lock lock(m_impl->mutex);
-  if ((uid - 1) >= m_impl->components.size() || !m_impl->components[uid - 1])
-    return nullptr;
-  return m_impl->components[uid - 1]->sendable;
-}
-
-void SendableRegistry::Publish(UID sendableUid,
-                               std::shared_ptr<NetworkTable> table) {
-  std::scoped_lock lock(m_impl->mutex);
-  if (sendableUid == 0 || (sendableUid - 1) >= m_impl->components.size() ||
-      !m_impl->components[sendableUid - 1])
-    return;
-  auto& comp = *m_impl->components[sendableUid - 1];
-  comp.builder = SendableBuilderImpl{};  // clear any current builder
-  comp.builder.SetTable(table);
-  comp.sendable->InitSendable(comp.builder);
-  comp.builder.UpdateTable();
-  comp.builder.StartListeners();
-}
-
-void SendableRegistry::Update(UID sendableUid) {
-  if (sendableUid == 0) return;
-  std::scoped_lock lock(m_impl->mutex);
-  if ((sendableUid - 1) >= m_impl->components.size() ||
-      !m_impl->components[sendableUid - 1])
-    return;
-  m_impl->components[sendableUid - 1]->builder.UpdateTable();
-}
-
-void SendableRegistry::ForeachLiveWindow(
-    int dataHandle,
-    wpi::function_ref<void(CallbackData& data)> callback) const {
-  assert(dataHandle >= 0);
-  std::scoped_lock lock(m_impl->mutex);
-  wpi::SmallVector<Impl::Component*, 128> components;
-  for (auto&& comp : m_impl->components) components.emplace_back(comp.get());
-  for (auto comp : components) {
-    if (comp && comp->sendable && comp->liveWindow) {
-      if (static_cast<size_t>(dataHandle) >= comp->data.size())
-        comp->data.resize(dataHandle + 1);
-      CallbackData cbdata{comp->sendable,         comp->name,
-                          comp->subsystem,        comp->parent,
-                          comp->data[dataHandle], comp->builder};
-      callback(cbdata);
-    }
-  }
-}
-
-SendableRegistry::SendableRegistry() : m_impl(new Impl) {}
diff --git a/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp b/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp
index 5e70a4c..25bb61f 100644
--- a/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp
+++ b/wpilibc/src/main/native/cpp/smartdashboard/SmartDashboard.cpp
@@ -1,9 +1,6 @@
-/*----------------------------------------------------------------------------*/
-/* Copyright (c) 2011-2019 FIRST. All Rights Reserved.                        */
-/* Open Source Software - may be modified and shared by FRC teams. The code   */
-/* must be accompanied by the FIRST BSD license file in the root directory of */
-/* the project.                                                               */
-/*----------------------------------------------------------------------------*/
+// 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.
 
 #include "frc/smartdashboard/SmartDashboard.h"
 
@@ -12,252 +9,239 @@
 #include <networktables/NetworkTableInstance.h>
 #include <wpi/StringMap.h>
 #include <wpi/mutex.h>
+#include <wpi/sendable/SendableRegistry.h>
 
-#include "frc/WPIErrors.h"
-#include "frc/smartdashboard/SendableRegistry.h"
+#include "frc/Errors.h"
+#include "frc/smartdashboard/ListenerExecutor.h"
+#include "frc/smartdashboard/SendableBuilderImpl.h"
 
 using namespace frc;
 
 namespace {
-class Singleton {
- public:
-  static Singleton& GetInstance();
-
-  std::shared_ptr<nt::NetworkTable> table;
-  wpi::StringMap<SendableRegistry::UID> tablesToData;
+struct Instance {
+  detail::ListenerExecutor listenerExecutor;
+  std::shared_ptr<nt::NetworkTable> table =
+      nt::NetworkTableInstance::GetDefault().GetTable("SmartDashboard");
+  wpi::StringMap<wpi::SendableRegistry::UID> tablesToData;
   wpi::mutex tablesToDataMutex;
-
- private:
-  Singleton() {
-    table = nt::NetworkTableInstance::GetDefault().GetTable("SmartDashboard");
-    HAL_Report(HALUsageReporting::kResourceType_SmartDashboard, 0);
-  }
-  Singleton(const Singleton&) = delete;
-  Singleton& operator=(const Singleton&) = delete;
 };
-
 }  // namespace
 
-Singleton& Singleton::GetInstance() {
-  static Singleton instance;
+static Instance& GetInstance() {
+  HAL_Report(HALUsageReporting::kResourceType_SmartDashboard, 0);
+  static Instance instance;
   return instance;
 }
 
-void SmartDashboard::init() { Singleton::GetInstance(); }
+void SmartDashboard::init() {
+  GetInstance();
+}
 
-bool SmartDashboard::ContainsKey(wpi::StringRef key) {
-  return Singleton::GetInstance().table->ContainsKey(key);
+bool SmartDashboard::ContainsKey(std::string_view key) {
+  return GetInstance().table->ContainsKey(key);
 }
 
 std::vector<std::string> SmartDashboard::GetKeys(int types) {
-  return Singleton::GetInstance().table->GetKeys(types);
+  return GetInstance().table->GetKeys(types);
 }
 
-void SmartDashboard::SetPersistent(wpi::StringRef key) {
-  Singleton::GetInstance().table->GetEntry(key).SetPersistent();
+void SmartDashboard::SetPersistent(std::string_view key) {
+  GetInstance().table->GetEntry(key).SetPersistent();
 }
 
-void SmartDashboard::ClearPersistent(wpi::StringRef key) {
-  Singleton::GetInstance().table->GetEntry(key).ClearPersistent();
+void SmartDashboard::ClearPersistent(std::string_view key) {
+  GetInstance().table->GetEntry(key).ClearPersistent();
 }
 
-bool SmartDashboard::IsPersistent(wpi::StringRef key) {
-  return Singleton::GetInstance().table->GetEntry(key).IsPersistent();
+bool SmartDashboard::IsPersistent(std::string_view key) {
+  return GetInstance().table->GetEntry(key).IsPersistent();
 }
 
-void SmartDashboard::SetFlags(wpi::StringRef key, unsigned int flags) {
-  Singleton::GetInstance().table->GetEntry(key).SetFlags(flags);
+void SmartDashboard::SetFlags(std::string_view key, unsigned int flags) {
+  GetInstance().table->GetEntry(key).SetFlags(flags);
 }
 
-void SmartDashboard::ClearFlags(wpi::StringRef key, unsigned int flags) {
-  Singleton::GetInstance().table->GetEntry(key).ClearFlags(flags);
+void SmartDashboard::ClearFlags(std::string_view key, unsigned int flags) {
+  GetInstance().table->GetEntry(key).ClearFlags(flags);
 }
 
-unsigned int SmartDashboard::GetFlags(wpi::StringRef key) {
-  return Singleton::GetInstance().table->GetEntry(key).GetFlags();
+unsigned int SmartDashboard::GetFlags(std::string_view key) {
+  return GetInstance().table->GetEntry(key).GetFlags();
 }
 
-void SmartDashboard::Delete(wpi::StringRef key) {
-  Singleton::GetInstance().table->Delete(key);
+void SmartDashboard::Delete(std::string_view key) {
+  GetInstance().table->Delete(key);
 }
 
-nt::NetworkTableEntry SmartDashboard::GetEntry(wpi::StringRef key) {
-  return Singleton::GetInstance().table->GetEntry(key);
+nt::NetworkTableEntry SmartDashboard::GetEntry(std::string_view key) {
+  return GetInstance().table->GetEntry(key);
 }
 
-void SmartDashboard::PutData(wpi::StringRef key, Sendable* data) {
-  if (data == nullptr) {
-    wpi_setGlobalWPIErrorWithContext(NullParameter, "value");
-    return;
+void SmartDashboard::PutData(std::string_view key, wpi::Sendable* data) {
+  if (!data) {
+    throw FRC_MakeError(err::NullParameter, "{}", "value");
   }
-  auto& inst = Singleton::GetInstance();
+  auto& inst = GetInstance();
   std::scoped_lock lock(inst.tablesToDataMutex);
   auto& uid = inst.tablesToData[key];
-  auto& registry = SendableRegistry::GetInstance();
-  Sendable* sddata = registry.GetSendable(uid);
+  wpi::Sendable* sddata = wpi::SendableRegistry::GetSendable(uid);
   if (sddata != data) {
-    uid = registry.GetUniqueId(data);
+    uid = wpi::SendableRegistry::GetUniqueId(data);
     auto dataTable = inst.table->GetSubTable(key);
-    registry.Publish(uid, dataTable);
+    auto builder = std::make_unique<SendableBuilderImpl>();
+    auto builderPtr = builder.get();
+    builderPtr->SetTable(dataTable);
+    wpi::SendableRegistry::Publish(uid, std::move(builder));
+    builderPtr->StartListeners();
     dataTable->GetEntry(".name").SetString(key);
   }
 }
 
-void SmartDashboard::PutData(Sendable* value) {
-  if (value == nullptr) {
-    wpi_setGlobalWPIErrorWithContext(NullParameter, "value");
-    return;
+void SmartDashboard::PutData(wpi::Sendable* value) {
+  if (!value) {
+    throw FRC_MakeError(err::NullParameter, "{}", "value");
   }
-  auto name = SendableRegistry::GetInstance().GetName(value);
-  if (!name.empty()) PutData(name, value);
+  auto name = wpi::SendableRegistry::GetName(value);
+  if (!name.empty()) {
+    PutData(name, value);
+  }
 }
 
-Sendable* SmartDashboard::GetData(wpi::StringRef key) {
-  auto& inst = Singleton::GetInstance();
+wpi::Sendable* SmartDashboard::GetData(std::string_view key) {
+  auto& inst = GetInstance();
   std::scoped_lock lock(inst.tablesToDataMutex);
   auto it = inst.tablesToData.find(key);
   if (it == inst.tablesToData.end()) {
-    wpi_setGlobalWPIErrorWithContext(SmartDashboardMissingKey, key);
-    return nullptr;
+    throw FRC_MakeError(err::SmartDashboardMissingKey, "{}", key);
   }
-  return SendableRegistry::GetInstance().GetSendable(it->getValue());
+  return wpi::SendableRegistry::GetSendable(it->getValue());
 }
 
-bool SmartDashboard::PutBoolean(wpi::StringRef keyName, bool value) {
-  return Singleton::GetInstance().table->GetEntry(keyName).SetBoolean(value);
+bool SmartDashboard::PutBoolean(std::string_view keyName, bool value) {
+  return GetInstance().table->GetEntry(keyName).SetBoolean(value);
 }
 
-bool SmartDashboard::SetDefaultBoolean(wpi::StringRef key, bool defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDefaultBoolean(
-      defaultValue);
+bool SmartDashboard::SetDefaultBoolean(std::string_view key,
+                                       bool defaultValue) {
+  return GetInstance().table->GetEntry(key).SetDefaultBoolean(defaultValue);
 }
 
-bool SmartDashboard::GetBoolean(wpi::StringRef keyName, bool defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(keyName).GetBoolean(
-      defaultValue);
+bool SmartDashboard::GetBoolean(std::string_view keyName, bool defaultValue) {
+  return GetInstance().table->GetEntry(keyName).GetBoolean(defaultValue);
 }
 
-bool SmartDashboard::PutNumber(wpi::StringRef keyName, double value) {
-  return Singleton::GetInstance().table->GetEntry(keyName).SetDouble(value);
+bool SmartDashboard::PutNumber(std::string_view keyName, double value) {
+  return GetInstance().table->GetEntry(keyName).SetDouble(value);
 }
 
-bool SmartDashboard::SetDefaultNumber(wpi::StringRef key, double defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDefaultDouble(
-      defaultValue);
+bool SmartDashboard::SetDefaultNumber(std::string_view key,
+                                      double defaultValue) {
+  return GetInstance().table->GetEntry(key).SetDefaultDouble(defaultValue);
 }
 
-double SmartDashboard::GetNumber(wpi::StringRef keyName, double defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(keyName).GetDouble(
-      defaultValue);
+double SmartDashboard::GetNumber(std::string_view keyName,
+                                 double defaultValue) {
+  return GetInstance().table->GetEntry(keyName).GetDouble(defaultValue);
 }
 
-bool SmartDashboard::PutString(wpi::StringRef keyName, wpi::StringRef value) {
-  return Singleton::GetInstance().table->GetEntry(keyName).SetString(value);
+bool SmartDashboard::PutString(std::string_view keyName,
+                               std::string_view value) {
+  return GetInstance().table->GetEntry(keyName).SetString(value);
 }
 
-bool SmartDashboard::SetDefaultString(wpi::StringRef key,
-                                      wpi::StringRef defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDefaultString(
-      defaultValue);
+bool SmartDashboard::SetDefaultString(std::string_view key,
+                                      std::string_view defaultValue) {
+  return GetInstance().table->GetEntry(key).SetDefaultString(defaultValue);
 }
 
-std::string SmartDashboard::GetString(wpi::StringRef keyName,
-                                      wpi::StringRef defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(keyName).GetString(
-      defaultValue);
+std::string SmartDashboard::GetString(std::string_view keyName,
+                                      std::string_view defaultValue) {
+  return GetInstance().table->GetEntry(keyName).GetString(defaultValue);
 }
 
-bool SmartDashboard::PutBooleanArray(wpi::StringRef key,
-                                     wpi::ArrayRef<int> value) {
-  return Singleton::GetInstance().table->GetEntry(key).SetBooleanArray(value);
+bool SmartDashboard::PutBooleanArray(std::string_view key,
+                                     wpi::span<const int> value) {
+  return GetInstance().table->GetEntry(key).SetBooleanArray(value);
 }
 
-bool SmartDashboard::SetDefaultBooleanArray(wpi::StringRef key,
-                                            wpi::ArrayRef<int> defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDefaultBooleanArray(
+bool SmartDashboard::SetDefaultBooleanArray(std::string_view key,
+                                            wpi::span<const int> defaultValue) {
+  return GetInstance().table->GetEntry(key).SetDefaultBooleanArray(
       defaultValue);
 }
 
 std::vector<int> SmartDashboard::GetBooleanArray(
-    wpi::StringRef key, wpi::ArrayRef<int> defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).GetBooleanArray(
-      defaultValue);
+    std::string_view key, wpi::span<const int> defaultValue) {
+  return GetInstance().table->GetEntry(key).GetBooleanArray(defaultValue);
 }
 
-bool SmartDashboard::PutNumberArray(wpi::StringRef key,
-                                    wpi::ArrayRef<double> value) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDoubleArray(value);
+bool SmartDashboard::PutNumberArray(std::string_view key,
+                                    wpi::span<const double> value) {
+  return GetInstance().table->GetEntry(key).SetDoubleArray(value);
 }
 
-bool SmartDashboard::SetDefaultNumberArray(wpi::StringRef key,
-                                           wpi::ArrayRef<double> defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDefaultDoubleArray(
-      defaultValue);
+bool SmartDashboard::SetDefaultNumberArray(
+    std::string_view key, wpi::span<const double> defaultValue) {
+  return GetInstance().table->GetEntry(key).SetDefaultDoubleArray(defaultValue);
 }
 
 std::vector<double> SmartDashboard::GetNumberArray(
-    wpi::StringRef key, wpi::ArrayRef<double> defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).GetDoubleArray(
-      defaultValue);
+    std::string_view key, wpi::span<const double> defaultValue) {
+  return GetInstance().table->GetEntry(key).GetDoubleArray(defaultValue);
 }
 
-bool SmartDashboard::PutStringArray(wpi::StringRef key,
-                                    wpi::ArrayRef<std::string> value) {
-  return Singleton::GetInstance().table->GetEntry(key).SetStringArray(value);
+bool SmartDashboard::PutStringArray(std::string_view key,
+                                    wpi::span<const std::string> value) {
+  return GetInstance().table->GetEntry(key).SetStringArray(value);
 }
 
 bool SmartDashboard::SetDefaultStringArray(
-    wpi::StringRef key, wpi::ArrayRef<std::string> defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDefaultStringArray(
-      defaultValue);
+    std::string_view key, wpi::span<const std::string> defaultValue) {
+  return GetInstance().table->GetEntry(key).SetDefaultStringArray(defaultValue);
 }
 
 std::vector<std::string> SmartDashboard::GetStringArray(
-    wpi::StringRef key, wpi::ArrayRef<std::string> defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).GetStringArray(
-      defaultValue);
+    std::string_view key, wpi::span<const std::string> defaultValue) {
+  return GetInstance().table->GetEntry(key).GetStringArray(defaultValue);
 }
 
-bool SmartDashboard::PutRaw(wpi::StringRef key, wpi::StringRef value) {
-  return Singleton::GetInstance().table->GetEntry(key).SetRaw(value);
+bool SmartDashboard::PutRaw(std::string_view key, std::string_view value) {
+  return GetInstance().table->GetEntry(key).SetRaw(value);
 }
 
-bool SmartDashboard::SetDefaultRaw(wpi::StringRef key,
-                                   wpi::StringRef defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDefaultRaw(
-      defaultValue);
+bool SmartDashboard::SetDefaultRaw(std::string_view key,
+                                   std::string_view defaultValue) {
+  return GetInstance().table->GetEntry(key).SetDefaultRaw(defaultValue);
 }
 
-std::string SmartDashboard::GetRaw(wpi::StringRef key,
-                                   wpi::StringRef defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).GetRaw(defaultValue);
+std::string SmartDashboard::GetRaw(std::string_view key,
+                                   std::string_view defaultValue) {
+  return GetInstance().table->GetEntry(key).GetRaw(defaultValue);
 }
 
-bool SmartDashboard::PutValue(wpi::StringRef keyName,
+bool SmartDashboard::PutValue(std::string_view keyName,
                               std::shared_ptr<nt::Value> value) {
-  return Singleton::GetInstance().table->GetEntry(keyName).SetValue(value);
+  return GetInstance().table->GetEntry(keyName).SetValue(value);
 }
 
-bool SmartDashboard::SetDefaultValue(wpi::StringRef key,
+bool SmartDashboard::SetDefaultValue(std::string_view key,
                                      std::shared_ptr<nt::Value> defaultValue) {
-  return Singleton::GetInstance().table->GetEntry(key).SetDefaultValue(
-      defaultValue);
+  return GetInstance().table->GetEntry(key).SetDefaultValue(defaultValue);
 }
 
-std::shared_ptr<nt::Value> SmartDashboard::GetValue(wpi::StringRef keyName) {
-  return Singleton::GetInstance().table->GetEntry(keyName).GetValue();
+std::shared_ptr<nt::Value> SmartDashboard::GetValue(std::string_view keyName) {
+  return GetInstance().table->GetEntry(keyName).GetValue();
 }
 
-detail::ListenerExecutor SmartDashboard::listenerExecutor;
-
 void SmartDashboard::PostListenerTask(std::function<void()> task) {
-  listenerExecutor.Execute(task);
+  GetInstance().listenerExecutor.Execute(task);
 }
 
 void SmartDashboard::UpdateValues() {
-  auto& registry = SendableRegistry::GetInstance();
-  auto& inst = Singleton::GetInstance();
+  auto& inst = GetInstance();
+  inst.listenerExecutor.RunListenerTasks();
   std::scoped_lock lock(inst.tablesToDataMutex);
-  for (auto& i : inst.tablesToData) registry.Update(i.getValue());
-  listenerExecutor.RunListenerTasks();
+  for (auto& i : inst.tablesToData) {
+    wpi::SendableRegistry::Update(i.getValue());
+  }
 }