Skip to content

Controller

Namespace: CtrlDS4

class Controller

Emulated DS4 controller.

Call begin() once from setup(), then update() on every iteration of loop(). Drive the controls by binding buttons to pins with bind(), or set them from the sketch with the setters (setButton(), setStick(), setTrigger()): update() reports the latest state to the host.

Declare a single Controller in a sketch, since the board presents itself as one DS4.

CtrlDS4::Controller controller;

void setup() {
  controller.bind(CtrlDS4::Cross, 2);
  controller.begin();
}

void loop() {
  controller.update();
}

Type aliases

RumbleCallback

using RumbleCallback = void (*)(uint8_t strongMotor, uint8_t weakMotor)

Function called when the host changes the rumble.

Parameters

  • strongMotor: New speed of the StrongMotor.
  • weakMotor: New speed of the WeakMotor.

LightbarCallback

using LightbarCallback = void (*)(const LightbarColor &color)

Function called when the host changes the color of the lightbar.

Parameters

  • color: New color of the lightbar.

LightbarBlinkCallback

using LightbarBlinkCallback = void (*)(const LightbarBlink &blink)

Function called when the host changes the blinking of the lightbar.

Parameters

  • blink: New blinking of the lightbar.

Methods

begin()

bool begin()

Chooses the MAC address, then connects to the host as a DS4.

Call once from setup(). It removes the USB serial port unless setUsbSerialEnabled() keeps it, and briefly disconnects the board from the host, so the host sees it again as a DS4: open the Serial Monitor after begin(), or reopen it.

Returns

true if the board could present itself as a DS4, false otherwise.

update()

void update()

Exchanges the latest state with the host.

Reads the pins of the bound controls, applies the rumble and lightbar changes requested by the host (calling the callbacks set with onRumble(), onLightbarColor() and onLightbarBlink()), then sends the state of the controls once per report interval (see setReportInterval()).

Call on every iteration of loop(), without blocking it for long: the host expects a new report every few milliseconds. Does nothing until begin() has succeeded.

isReady()

bool isReady() const

Tells whether the library is ready to use.

Returns

true once begin() has completed successfully, false otherwise.

setReportInterval(uint16_t)

void setReportInterval(uint16_t intervalMs)

Sets how often update() sends the state of the controls to the host.

The host polls the controller every millisecond, so a report reaches it at most 1 ms after update() sends it.

Parameters

  • intervalMs: Time between two reports in milliseconds, DEFAULT_REPORT_INTERVAL_MS until set. Lower it for less latency, at the cost of more USB traffic. 0 sends a report at every poll of the host.

setUsbSerialEnabled(bool)

void setUsbSerialEnabled(bool isEnabled)

Keeps or removes the USB serial port of the board (Serial) next to the controller.

A real DS4 has no serial port, and some hosts refuse a controller that has one, so begin() removes it unless it's kept here. Without it, Serial prints nothing, and uploading a new sketch needs the board to be put in bootloader mode by hand.

Call it before begin(), which applies it. While the port is removed, don't call Serial.begin() after begin(): it would add the port back while the host already knows the device.

Parameters

  • isEnabled: true to keep the serial port, e.g. to print to the Serial Monitor while developing. false to remove it, the default.

setButton(Button, bool)

void setButton(Button button, bool isPressed)

Presses or releases a button.

A button bound to a pin (see bind()) follows its pin instead: the next update() overrides the value set here.

Parameters

  • button: Button to change. Ignored if it's not a Button value.
  • isPressed: true to press the button, false to release it.

setStick(Stick, uint8_t, uint8_t)

void setStick(Stick stick, uint8_t x, uint8_t y)

Moves a stick, with the values of the DS4 report.

See setAxis() to move a single axis with a value from -1 to 1. An axis bound to pins (see bind()) follows its pins instead: the next update() overrides the value set here.

Parameters

  • stick: Stick to move. Ignored if it's not a Stick value.
  • x: Horizontal position, from STICK_MIN (left) to STICK_MAX (right).
  • y: Vertical position, from STICK_MIN (up) to STICK_MAX (down), as in the DS4 report.

setAxis(StickAxis, float)

void setAxis(StickAxis axis, float value)

Moves a single axis of a stick.

An axis bound to pins (see bind()) follows its pins instead: the next update() overrides the value set here.

controller.setAxis(CtrlDS4::LeftStickX, 1);     // Left stick fully right
controller.setAxis(CtrlDS4::LeftStickY, -0.5);  // Left stick halfway down

Parameters

  • axis: Axis to move. Ignored if it's not a StickAxis value.
  • value: Position on the axis, from -1 to 1, 0 being at rest: X goes from left (-1) to right (1), and Y from down (-1) to up (1). Clamped to that range.

setTrigger(Trigger, uint8_t)

void setTrigger(Trigger trigger, uint8_t value)

Presses a trigger, with the values of the DS4 report.

The trigger is also reported as a pressed button as soon as its value is above TRIGGER_MIN, as a real DS4 does. See setTriggerValue() to press it with a value from 0 to 1. A trigger bound to a pin (see bind()) follows its pin instead: the next update() overrides the value set here.

Parameters

  • trigger: Trigger to press. Ignored if it's not a Trigger value.
  • value: How far the trigger is pressed, from TRIGGER_MIN (released) to TRIGGER_MAX (fully pressed).

setTriggerValue(Trigger, float)

void setTriggerValue(Trigger trigger, float value)

Presses a trigger.

The trigger is also reported as a pressed button as soon as its value is above 0, as a real DS4 does. A trigger bound to a pin (see bind()) follows its pin instead: the next update() overrides the value set here.

controller.setTriggerValue(CtrlDS4::R2, 0.5);  // R2 pressed halfway

Parameters

  • trigger: Trigger to press. Ignored if it's not a Trigger value.
  • value: How far the trigger is pressed, from 0 (released) to 1 (fully pressed). Clamped to that range.

releaseAll()

void releaseAll()

Puts all the controls at rest: buttons released, sticks centered and triggers released.

bind(Button, uint8_t, ButtonWiring)

void bind(Button button, uint8_t pin, ButtonWiring wiring = ActiveLow)

Drives a button from a pin of the board.

Sets the mode of the pin for the wiring, then update() reads it and presses or releases the button, filtering the bounces of the contacts (see setDebounceTime()).

controller.bind(CtrlDS4::Cross, 2);                       // Button between pin 2 and GND
controller.bind(CtrlDS4::Circle, 3, CtrlDS4::ActiveHigh);  // Button between pin 3 and 3.3V

Parameters

  • button: Button to drive. Ignored if it's not a Button value. Binding it again replaces its previous pin.
  • pin: Pin the button is wired to.
  • wiring: How the button is wired to the pin.

unbind(Button)

void unbind(Button button)

Stops driving a button from its pin, and releases it.

The mode of the pin is left as is.

Parameters

  • button: Button to stop driving. Ignored if it's not a Button value, or not bound.

bind(Trigger, uint8_t, ButtonWiring)

void bind(Trigger trigger, uint8_t pin, ButtonWiring wiring = ActiveLow)

Drives a trigger from a button wired to a pin of the board.

The trigger is fully pressed while the button is pressed, and released otherwise.

controller.bind(CtrlDS4::R2, 4);  // Button between pin 4 and GND

Parameters

  • trigger: Trigger to drive. Ignored if it's not a Trigger value. Binding it again replaces its previous pin.
  • pin: Pin the button is wired to.
  • wiring: How the button is wired to the pin.

bind(StickAxis, AxisHalf, uint8_t, ButtonWiring)

void bind(StickAxis axis, AxisHalf half, uint8_t pin, ButtonWiring wiring = ActiveLow)

Drives a half of a stick axis from a button wired to a pin of the board.

The axis is pushed to the end of that half while the button is pressed. Bind a button to each half to drive the whole axis: pressed together, they cancel each other.

controller.bind(CtrlDS4::LeftStickY, CtrlDS4::Positive, 5);  // Up while pressed
controller.bind(CtrlDS4::LeftStickY, CtrlDS4::Negative, 6);  // Down while pressed

Parameters

  • axis: Axis to drive. Ignored if it's not a StickAxis value. Replaces the analog pin of the whole axis, if any (see bindAnalog()).
  • half: Half of the axis the button pushes to. Ignored if it's not an AxisHalf value. Binding it again replaces its previous pin.
  • pin: Pin the button is wired to.
  • wiring: How the button is wired to the pin.

bindAnalog(Trigger, uint8_t, const AnalogCalibration &)

void bindAnalog(Trigger trigger, uint8_t pin, const AnalogCalibration &calibration = AnalogCalibration())

Drives a trigger from an analog pin of the board.

The trigger follows the reading of the pin: released at AnalogCalibration::minReading, fully pressed at AnalogCalibration::maxReading. The pin is read on the update() calls that send a report to the host.

controller.bindAnalog(CtrlDS4::R2, A0);                                     // Whole range
controller.bindAnalog(CtrlDS4::L2, A1, CtrlDS4::AnalogCalibration(80, 950));  // Calibrated

Parameters

  • trigger: Trigger to drive. Ignored if it's not a Trigger value. Binding it again replaces its previous pin.
  • pin: Analog pin to read.
  • calibration: Readings that match the released and fully pressed trigger, and deadzone near the released trigger. The whole analogRead() range by default.

bindAnalog(StickAxis, uint8_t, const AnalogCalibration &)

void bindAnalog(StickAxis axis, uint8_t pin, const AnalogCalibration &calibration = AnalogCalibration())

Drives a whole stick axis from an analog pin of the board, e.g. a potentiometer.

The axis follows the reading of the pin: left (X) or down (Y) at AnalogCalibration::minReading, right (X) or up (Y) at AnalogCalibration::maxReading, and centered halfway. The pin is read on the update() calls that send a report to the host.

controller.bindAnalog(CtrlDS4::LeftStickX, A0, CtrlDS4::AnalogCalibration(0, 1023, 0.05));
controller.bindAnalog(CtrlDS4::LeftStickY, A1, CtrlDS4::AnalogCalibration(1023, 0, 0.05));  // Inverted

Parameters

  • axis: Axis to drive. Ignored if it's not a StickAxis value. Replaces the previous pins of the axis, including the ones of its halves.
  • pin: Analog pin to read.
  • calibration: Readings that match both ends of the axis, and deadzone around the center. The whole analogRead() range by default.

bindAnalog(StickAxis, AxisHalf, uint8_t, const AnalogCalibration &)

void bindAnalog(StickAxis axis, AxisHalf half, uint8_t pin, const AnalogCalibration &calibration = AnalogCalibration())

Drives a half of a stick axis from an analog pin of the board, e.g. a pressure sensor.

The axis follows the reading of the pin, from rest at AnalogCalibration::minReading to the end of the half at AnalogCalibration::maxReading. Bind the other half to drive the whole axis: the axis value is the positive half minus the negative half. The pin is read on the update() calls that send a report to the host.

controller.bindAnalog(CtrlDS4::RightStickX, CtrlDS4::Positive, A0);  // Pushes right
controller.bindAnalog(CtrlDS4::RightStickX, CtrlDS4::Negative, A1);  // Pushes left

Parameters

  • axis: Axis to drive. Ignored if it's not a StickAxis value. Replaces the analog pin of the whole axis, if any.
  • half: Half of the axis the pin drives. Ignored if it's not an AxisHalf value. Binding it again replaces its previous pin.
  • pin: Analog pin to read.
  • calibration: Readings that match rest and the end of the half, and deadzone near rest. The whole analogRead() range by default.

unbind(Trigger)

void unbind(Trigger trigger)

Stops driving a trigger from its pin, and releases it.

The mode of the pin is left as is.

Parameters

  • trigger: Trigger to stop driving. Ignored if it's not a Trigger value, or not bound.

unbind(StickAxis)

void unbind(StickAxis axis)

Stops driving a stick axis from its pins (whole axis and both halves), and puts the axis at rest.

The mode of the pins is left as is.

Parameters

  • axis: Axis to stop driving. Ignored if it's not a StickAxis value, or not bound.

setDebounceTime(uint16_t)

void setDebounceTime(uint16_t debounceMs)

Sets how long the bound buttons ignore their pin after a press or a release.

Applies to all the buttons wired to pins, including the ones driving a trigger or a stick axis.

Mechanical buttons bounce for a few milliseconds, which would read as several presses. Presses are still reported at once: this only limits how fast a button can be pressed again.

Parameters

  • debounceMs: Duration in milliseconds, DEFAULT_DEBOUNCE_MS until set. Raise it if a button reports double presses, lower it for very fast repeated presses. 0 disables the filter.

setMacAddress(const MacAddress &)

void setMacAddress(const MacAddress &macAddress)

Sets the MAC address reported to the host, instead of the one begin() chooses.

Call it before begin(), since the host reads the address when the controller connects.

Parameters

  • macAddress: Address to report. See MacAddress for the bits to set and clear.

getMacAddress()

const MacAddress &getMacAddress() const

Gets the MAC address reported to the host.

Unless set with setMacAddress(), it's chosen by begin(): derived from the factory unique ID of the board, or a fixed fallback address if the board has none.

Returns

The MAC address. Only meaningful once begin() has been called, or setMacAddress().

getMacAddressSource()

MacAddressSource getMacAddressSource() const

Tells where the MAC address comes from.

Use it to warn when the board has no usable unique ID, since the fallback address is the same on all those boards.

Returns

Where the address returned by getMacAddress() comes from.

getRumble(Motor)

uint8_t getRumble(Motor motor) const

Gets the speed of a rumble motor, as last requested by the host.

Parameters

Returns

Speed of the motor, from 0 (stopped) to 255 (full speed). 0 if it's not a Motor value.

getLightbarColor()

LightbarColor getLightbarColor() const

Gets the color of the lightbar, as last requested by the host.

Returns

Color of the lightbar. Off until the host sets it.

LightbarBlink getLightbarBlink() const

Gets the blinking of the lightbar, as last requested by the host.

Returns

Blinking of the lightbar. Not blinking until the host sets it.

onRumble(RumbleCallback)

void onRumble(RumbleCallback callback)

Sets the function to call when the host changes the rumble.

The function is called from update(), so it can safely drive pins or print to Serial.

Parameters

  • callback: Function to call, or nullptr to stop calling one.

onLightbarColor(LightbarCallback)

void onLightbarColor(LightbarCallback callback)

Sets the function to call when the host changes the color of the lightbar.

The function is called from update(), so it can safely drive pins or print to Serial.

Parameters

  • callback: Function to call, or nullptr to stop calling one.

onLightbarBlink(LightbarBlinkCallback)

void onLightbarBlink(LightbarBlinkCallback callback)

Sets the function to call when the host changes the blinking of the lightbar.

The function is called from update(), so it can safely drive pins or print to Serial.

Parameters

  • callback: Function to call, or nullptr to stop calling one.