Pico+ SixAxis Motion Sensor

The Pixelblaze V3 Pico+ (product page) has a built-in six-axis motion sensor: a 3-axis accelerometer and a 3-axis gyroscope. Patterns read it through one array, sixAxis.

  1. Detecting the sensor
  2. Code
  3. Readings and units
  4. Axes and mounting
  5. Sensor Expansion Board compatibility
  6. Example code

Detecting the sensor

The status area in the top right of the web app lists detected expansion hardware. A Pico+ shows "6 Axis" there. The sensor is detected at boot and needs no settings.

Code

Declare and export the sixAxis variable. Pixelblaze fills the array before each beforeRender() call.

export var sixAxis = array(6)
IndexReadingUnit
sixAxis[0]Acceleration Xg
sixAxis[1]Acceleration Yg
sixAxis[2]Acceleration Zg
sixAxis[3]Angular velocity Xdegrees per second
sixAxis[4]Angular velocity Ydegrees per second
sixAxis[5]Angular velocity Zdegrees per second

Use the Vars Watch panel in the Edit tab to see the live values while you move the Pico+.

Creating the array with array(6) is the safe form. On a Pico without the sensor, or on a V3 Standard, sixAxis is not written and the array keeps the values you give it. A plain export var sixAxis with no array is also accepted and is filled in on a Pico+.

Readings and units

Accelerometer. Values are in g (standard gravity), with a full scale of plus or minus 16 g. At rest, the axis pointing down reads close to 1.0 or -1.0 and the other two read close to 0. Shaking by hand reaches several g. Acceleration includes gravity, so a stationary tilted board reads a steady, non-zero vector.

Gyroscope. Values are in degrees per second, with a full scale of plus or minus 2000 degrees per second. At rest the three values read close to 0 with a small constant offset that differs from board to board and from boot to boot. For patterns that integrate rotation over time, measure this offset while the board is still and subtract it.

There is no magnetometer. Rotation about the vertical axis (heading) has no absolute reference and can only be tracked by integrating the gyroscope, which drifts. Tilt (pitch and roll) can be corrected against gravity using the accelerometer.

Axes and mounting

The sensor's X, Y, and Z axes are fixed to the Pico+ board. How they line up with your LEDs depends on how the board is mounted. Patterns that depend on axis direction usually expose slider or toggle controls to flip or swap axes. Watch the values with Vars Watch while tilting the board along each axis to find out which index responds.

Forum member sorceror reported these directions on a Pico+:

  • sixAxis[0]: across the board, positive when the side with the status LED is up
  • sixAxis[1]: along the board, positive when the connector end is up
  • sixAxis[2]: normal to the board, positive when the chip side is up
  • sixAxis[3], sixAxis[4], sixAxis[5]: rotation about the same three axes, positive for counterclockwise rotation

Sensor Expansion Board compatibility

The Sensor Expansion Board provides a 3-axis accelerometer array. On a Pico+, patterns that export accelerometer also work: the firmware fills it from the built-in sensor, scaled to the same range as the sensor board. 1 g reads about 0.020, the same as on the sensor board. Existing sensor board patterns like "Shake to switch modes" run on a Pico+ unchanged.

If a Sensor Expansion Board is also connected, its accelerometer takes over accelerometer. sixAxis always comes from the built-in sensor.

Example code

Gyro rotation tracking

This pattern by forum member Irwin integrates one gyroscope axis to track the rotation angle of a spinning object, such as a bicycle wheel or a parasol, and counter-rotates a 2D mapped image so it appears to stand still. It was written for a Pico+ mounted on a wheel and later used on a parasol handle with sixAxis[4] in place of sixAxis[5]. See the wheel position and parasol threads.

// Modified from "Accelerometer level example" pattern

export var sixAxis
export var gyro
export var currentAngleDegs
export var currentAngleRads

export function beforeRender(delta) {
  
  // get the current value of the Z gyro
  gyro = sixAxis[5]
  
  // add error correction (varies by device)
  gyro += -0.9

  // calculate the current wheel angle in degrees
  currentAngleDegs = mod (currentAngleDegs + ((gyro * (delta) / 1000)), 360)
  
  // convert angle from degrees to rads
  currentAngleRads = currentAngleDegs * (PI / 180) 

  //you may need to reverse this angledepending on the 
  //sensor board orientation relative to the LEDs
  //currentAngleRads = -currentAngleRads
  
  //apply this angle as a rotation
  //first reset the transform
  resetTransform ()
  //center coordinates so we rotate around the center, not the corner
  translate (-.5, -.5) 
  //rotate by the correction angle
  rotate (currentAngleRads)
}

export function render2D(index, x, y) {
  //draw a vertical line
  var v = clamp (1 - abs (y * 5), 0, 1)
  hsv (x + .5, 1, v * v)
}

The gyro += -0.9 line subtracts that board's resting gyro offset. Read your own offset in Vars Watch with the board still and use that value.

Shake to switch modes

This is the Sensor Expansion Board's shake example rewritten to read sixAxis in g.

// Shake to switch modes using the six axis accelerometer

export var sixAxis = array(6)
mode = debounce = 0

export function beforeRender(delta) {
  // Magnitude of acceleration in g. Gravity alone reads 1.0
  totalAcceleration = hypot3(sixAxis[0], sixAxis[1], sixAxis[2])

  debounce = clamp(debounce + delta, 0, 2000) // Prevent overflow

  // Cycle mode if the board is shaken, no more than 1x / sec
  if (debounce > 1000 && totalAcceleration > 1.5) {
    mode = (mode + 1) % 3
    debounce = 0
  }
}

h = s = v = 1
modes = array(3)
modes[0] = (index) => { h = 1 - index / pixelCount / 4; v = 1 }
modes[1] = (index) => { h = 0.5; v = index % 2 }
modes[2] = (index) => { h = wave((index+wave(time(0.04))*5)/pixelCount)*6; v = 0.1+random(wave(time(0.03))) }

export function render(index) {
  modes[mode](index)
  hsv(h, s, v)
}