General Purpose Input/Output (GPIO)

  1. Introduction
  2. Capabilities per model
  3. Using the ADC
  4. Digital IO
  5. Analog out hack
Aesthetic macro shot with some areas out of focus of the Pixelblaze v3 Standard's back side. The back side, or 'underside', is where the GPIO solder pads are.

Introduction

GPIO is how Pixelblaze can send or receive electrical signals from the pins labeled "IOXX".

GPIO pin(s) are part of all Pixelblaze boards. When you need more inputs, consider the Sensor Expansion Board. It includes 5 analog/digital inputs, audio level and frequency analysis with mic and line-in, 3-axis acclerometer, and a light sensor.

Pixelblaze can:

  1. Read analog voltages using ADC pins. These can be used to read the value of a physical rotary knob or linear fader.
  2. Read and write digital values to HIGH and LOW voltages on GPIO pins
  3. Output analog values via a WS2811 chip hack

For other types of IO and control, consider the UI Controls that patterns can expose in the Pixelblaze app, the Websockets API, or pixel-data output hacks like the WS2811 Extender or pixel-controlled relays.

The Pixelblaze language reference (on the Edit tab of the Pixelblaze app, or online here) has the most updated information.

Schematics

The latest circuit diagrams are at https://github.com/simap/pixelblaze. You'll find Eagle (.brd, .sch) and PDFs. For convenience, here is the Pixelblaze v3.4 Standard schematic.

Circuit schematic for the Pixelblaze v3 Standard

On-board IO capabilities

v3 Standard IO Pins

Back of the Pixelblaze v3 Standard, HW v3.6
Hardware revision v3.6
Back of the Pixelblaze v3 Standard, HW v3.4
Hardware revision v3.4
HW rev Location Pin label ESP32 pin1 Capabilities
Digital in Digital out Analog in Capacitive touch
All v3 8-pin header IO0 25 Y Y N N
IO25 10 Y Y N N
IO26 11 Y Y N N
Underside pad IO2/T2 24 Y Y N Y
IO4/T0 26 Y Y N Y
IO13/T4 16 Y Y N Y
T6/IO14 13 Y Y N Y
IO19 31 Y Y N N
IO21 33 Y Y N N
IO22 36 Y Y N N
IO27/T7 13 Y Y N Y
IO33 9 Y Y Y N
>=v3.5 (~Jan '22) IO16 27 Y Y N N
IO17 28 Y Y N N
IO34 6 Y2 N Y N
IO35 7 Y2 N Y N
IO36 4 Y2 N Y N
IO39 5 Y2 N Y N
1In Pixelblaze code, reference pins in pinMode(pin, mode), digitalRead(pin), etc. by the number after their label. For example, to write IO21 high, use digitalWrite(21, 1). The ESP32 pin numbers are provided here for reference to match the schematic and ESP datasheet.
2On the ESP32 that Pixelblaze uses, IO34, IO35, IO36, and IO39 do not support pinMode's of INPUT_PULLUP or INPUT_PULLDOWN. They are always a floating INPUT. This means that to read a button which has one state disconnected, you'll need to wire an external pullup or pulldown resistor. 10 KΩ works well.

v3 Pico IO Pin

Back of the Pixelblaze v3 Pico, showing solderable pads E, R, T, 3v, G, and 0.
HW rev Location Pin label ESP32 D4 pin Capabilities
Digital in Digital out Analog in Capacitive touch
All Underside pad IO0 23 Y - see below Y - see below N Y

The Pico exposes IO0 as a solderable pad labeled "0". As noted above, it cannot be used as an analog input for implementation reasons.

If it is held low during power on, it will put the ESP32-PICO-D4 chip into bootloader mode which will cause the board to not function.

IO0 is best used as a normally-high input, like a button, or as an active-low output since it will float high during boot before a pattern runs.

IO0 on the Pico can also be used as a capacitive touch sensor.

The Pico's underside copper pads labeled R, 3 and G (RX, 3.3 V, and Ground) can be connected to the Pixelblaze Sensor Board to gain access to 5 additional ADC inputs. That said, this doesn't make much sense in most scenarios, as the sensor board is larger than the Pico. Using a v3 Standard with a sensor board is usually a better option.

v2 IO Pins

Back of the Pixelblaze v2 showing IO pin solder pads.
HW rev Location Pin label ESP8266 12S pin Capabilities
Digital in Digital out Analog in Capacitive touch
<=v2.1 Underside pad GP12 6 Y Y N N
All v2 8-pin header ADC 2 N N Y N
IO0 12 Y Y N N
Underside pad GP2 11 Y* Y* N N
GP4 13 Y Y N N
GP5 14 Y Y N N
GP16 4 Y Y N N

Using the Analog to Digital Converter (ADC)

Pixelblaze v2 and v3 Standard have ADC inputs that can read voltages. For example, you could connect an ADC pin to a potentiometer (a variable resistor). This rotary knob can control something in your pattern, like the speed, brightness, pattern mode, or color palette.

v3 Standard v3 Pico v2
ADC inputs HW <=3.4: 1
HW >=3.5: 5
0* 1
Voltage input range 0-3.3 V n/a 0-1 V
Function in code analog­Read(pin) n/a readAdc()
* While IO0 is exposed as a solderable pad on the Pico's underside, IO0 is on the forbidden ADC2 peripheral which prevents its use as an analog input.

The ADCs on Pixelblaze v3 accept input voltages between 0 V (ground) and the ESP32 chip's VDD of 3.3 V. The ADC on Pixelblaze v2's ESP8266 can accept voltages between 0 and 1 V. To sense larger ranges you need to scale the input voltage down to the appropriate range.

The easist way to add a rotary input on the Pixelbalze v3 Standard is to connect a pot with the two outer contacts soldered to GND and the 3.3 V pins. The center contact is then soldered to IO33 on the underside.

analogRead(pin) returns values between 0 and 1.

Here's example code for using a knob on IO33 as a brightness control.

// Set the pin labeled IO33 to use its ADC (A-to-D Converter)
pinMode(33, ANALOG)

// Export this var to monitor the value live in the IDE's Vars Watch
export var knobVal

export function beforeRender(delta) {
  // Read the value once per frame
  // Values are in 0-1 for 0-3.3 V sensed on the pin
  knobVal = analogRead(33)
}

export function render(index) {
  // Scale the output brightness from 0 - 100% based on the sensed voltage
  hsv(0, 1, knobVal)
}

Voltage Dividers for Scaling ADC Input

When you need to scale an input voltage down to the ADC's allowed range, you'll need to select appropriate resistor values to make a voltage divider. The examples below are for scaling voltages higher than the ADC's max with a potentiometer.

Calculator

Use the following calculator to find the appropriate value for resistor R1, given the value of your potentiomoter. The calculator starts with example values for a scenario where your available VDD was 5V (for some reason you weren't able to use the 3v3 pad).

A voltage divider schematic for use with the following calculator. Vin connected to  resistor R1 in series with potentiometer VR1 to ground. Potentiometer tap is labeled Rout. There are no values.
V
V

Slightly higher values for R1 are OK.

Pixelblaze v2 ADC Example

A Pixelbalze v2 will need to scale down the 3.3 V from VDD down to no more than 1 V. The pot is connected between the 3v3 pin and GND, and the pot tap (center lead) is connected to the ADC pin.

A voltage divider composed of the 3.3 V Vdd connected to a 23 KΩ resistor in series with a 10 KΩ potentiometer to ground.
A voltage divider limits the pot's center tap output to 1v. R1 = 3.3 * VR1 - VR1; therefore a 23 KΩ resistor is called for in this example.

Slightly higher values for R1 are OK, for example using a standard 24 KΩ resistor instead of a 23 KΩ resistor will still keep the output voltage under 1 V — it will max out at 0.97 V instead of exactly 1 V.

To use this in a v2 pattern as a brightness control, multiply the brightness value argument by the analogRead() result:

hsv(h, s, v * analogRead())

Digital IO

If you're coming from Arduino, you may be familliar with pinMode(), digitalRead(), and digitalWrite().

pinMode(pin, mode) sets whether a given IO pin is set up as an input or an output. If it's an input, we can set its electrical characteristics, such as whether there is an internal pullup or pulldown resistor. Supported modes are INPUT, INPUT_PULLUP, INPUT_PULLDOWN, OUTPUT, OUTPUT_OPEN_DRAIN, or ANALOG.

On the ESP32 that Pixelblaze uses, IO34, IO35, IO36, and IO39 do not support pinMode's of INPUT_PULLUP or INPUT_PULLDOWN. They are always a floating INPUT. This means that to read a button which has one state disconnected, you'll need to wire an external pullup or pulldown resistor. 10 KΩ works well.

When a pin is set as an input, digitalRead() returns 1 if the pin's voltage is driven at 2.0 V or higher, otherwise it returns 0. If the pinMode is set to INPUT_PULLUP and the pin is not connected to a voltage, an internal resistor pulls the voltage up to 3.3V so the value returned will be 1. INPUT_PULLDOWN does the inverse.

When a pin is set as an output, digitalWrite(<1 or 0>) lets you set the voltage on the pin to 3.3 V or 0 V. Here's an example program that alternates toggling IO25 and IO26 on and off:

pinMode(25, OUTPUT) // Pin labeled IO25 set as an output
pinMode(26, OUTPUT)

export function beforeRender(delta) {
  t1 = time(2 / 65.535) // Ramp 0..1 every 2 seconds
  digitalWrite(25, t1 < .5) // Set IO25 high for the 1st second
  digitalWrite(26, t1 >= .5)
}

export function render(index) { hsv(0, 0, 0) }

Driving the onboard status LED

GPIO12 is connected to the onboard LED. On v2, you can digitalWrite(12, HIGH_OR_LOW) to turn the LED on or off. This isn't supported yet on v3. If you need to disable the LED because it's distracting where installed, we reccomend covering it with a small square of electrical tape.

Limiting the current drawn from an output

WARNING: Each of the IO pins can source 20 mA or sink 28mA of current.

According to the ESP32-WROOM-32 datasheet (page 15), each of the IO pins can source 20 mA or sink 28mA of current. This means that if you want to power something off an IO pin when the pin is in a HIGH state (set to 3.3 V), you will harm the chip if the circuit you're driving consumes more than 20 mA of current. In other words, by V=I*R, bad things happen if the driven circuit's resistance is less than 165 Ω.

This implies you should not directly drive things such as LEDs (many of which can draw 30mA) or relays (many of which can draw 100mA). Consult the datasheet for the device you wish to control. You can look up transistor and MOSFET circuits that allow you to drive larger loads.

There are a wide variety of pre-made boards that can help. For example, you can search for "3V relay control board" or "Arduino LED driver". At time of writing, you can find a 10 pack of relay boards for $16 that can each switch 10A at 250 VAC or 30 VDC while drawing only 3 mA from the Pixelblaze. This board would let you switch on/off a 350 mA (very high power) LED.

Hacking an Analog Output

On an Arduino, you may be familliar with the trick of using a PWM output via analogWrite(pin), filtered through an RC circuit.

Pixelblaze doesn't currently have an analogWrite() capability or PWM outputs, but you can hack something similar by using the fact that the LED chips we are typically driving are also PWM outputs.

ElectroMage and others sell discrete WS2811 chips. These can be wired inline with your other LEDs (assuming they also use the WS281X / "Neopixel" protocol). They take in three 8-bit values associated with a single pixel that would normally be used to drive the Red, Green, and Blue LEDs at >=400Hz PWM. Here's a writeup covering how to select appropriate resistor and capacitorvalues.

Using a single WS2811 chip you essentialy have three 8-bit analog outputs. Each would output between 0 and your LED's voltage, typically 5 V or 12 V, and this could be scaled down with a voltage divider.