CyberBrick Transmitter Shield (X12) - rotorman/CyberBrick_ESPNOW GitHub Wiki

By inspecting the original CyberBrick MicroPython code and the info provided at the CyberBrick Quick reference, I was able to extract the full ESP32-C3 pin-mapping (GPIO 0 to 21) for the transmitter side X12 shield.

The X12 shield has the following mapping to the CyberBrick core ESP32-C3 GPIO pins:

In addition (from Quick reference):

  • Pin 8 - NeoPixel LED on CyberBrick core PCB, pin not routed out of CyberBrick Core and thus not available on X12.
  • GPIO Pins 18 & 19 are used for REPL USB D- and D+ respectively
  • GPIO Pins 12 to 17 are used for connecting the embedded flash

Analog inputs expect a signal in the voltage range of 0 to +2.5V (with max. 11 dB attentuation, as in https://docs.espressif.com/projects/esp-idf/en/v4.4.2/esp32c3/api-reference/peripherals/adc.html). The X12 shield has 1 kOhm resistor in-between L(-eft) and R(-ight) analog inputs and CyberBrick Core pins in addition to GND connected filter capacitor (I did not de-solder the cap to figure out it's value).

The CyberBrick kit 3-position switch is connected to the 3-pin analog port, where one pin is GND, other +3.3V and third signal. The signal pin has two 10 kOhm resistors connected to it, one to GND and other to +3.3V. The switch in the middle is floating, thus half the +3.3V voltage is supplied via two 10 kOhm resistor voltage divider to the signal pin. By actuating the switch to either one or the other side, the signal pin is shorted either to GND or to +3.3V.

Digital inputs are similarly protected with a 1 kOhm resistor in series between the K pins and CyberBrick Core pins. In addition there is a filter cap to GND after the resistor, similar to analog inputs. In addition the digital pins are pulled high by a 18 kOhm resistor each on the CyberBrick Core side. The digital inputs expect to be shorted between GND and the signal pin.

Interestingly, the 5V pin of CyberBrick Core is not supplied with 5V by the X12 transmitter shield - I could measure only roughly +2V on that pin, whereas the battery pin was at over +8V with full battery and +3.3V pin correctly at +3.3V (+3.36V in my measurement).

The original CyberBrick rc_app MicroPython code does not include the source files for the transmitter side. We can only see frozen Python library rc_module being imported, rc_module.rc_master_init() called and a call inside an infite loop polling an undocumented rc_module.file_transfer() for any configuration updates.


I wrote a small test program (below) and checked the raw ADC values from the control elements included in the original CyberBrick Hardware Kit in a REPL terminal using the CyberBrick official standard remote hookup scheme:

from machine import Pin, ADC
from time import sleep

# Comments list controls as used by the CyberBrick official standard remote
l1 = ADC(Pin(0), atten=ADC.ATTN_11DB) # 3-way-switch
l2 = ADC(Pin(1), atten=ADC.ATTN_11DB) # Left horizontal (LH) stick
l3 = ADC(Pin(2), atten=ADC.ATTN_11DB) # Left vertical (LV) stick
r1 = ADC(Pin(3), atten=ADC.ATTN_11DB) # Slider
r2 = ADC(Pin(4), atten=ADC.ATTN_11DB) # RH
r3 = ADC(Pin(5), atten=ADC.ATTN_11DB) # RV

k1 = Pin(6, Pin.IN)  # Button
k2 = Pin(7, Pin.IN)  # Not used
k3 = Pin(21, Pin.IN) # Not used
k4 = Pin(20, Pin.IN) # Not used

while True:
  print("{};{};{}; {};{};{}; {};{};{};{}".format(l1.read(), l2.read(), l3.read(), r1.read(), r2.read(), r3.read(), k1.value(), k2.value(), k3.value(), k4.value()))
  sleep(0.5)

The results (CyberBrick Core has ESP32-C3 with 12-bit ADC, range 0 to 4095):

  • L1 (3-way-switch): right 8 to 20, middle 2297 to 2300, left 4095
  • L2 (Left horizontal (LH) stick): right 17 to 20, center 2042 to 2136, left 4093 to 4095
  • L3 (Left vertical (LV) stick): up 17 to 20, center 1931 to 1982, down 4095
  • R1 (Slider): up 8 to 18, center 1788 to 1835, down 4095
  • R2 (RH): right 7 to 18, center 1928 to 1992, left 4095
  • R3 (RV): up 5 to 8, center 1869 to 1964, down 4095

The good news is that the full ADC range is nicely used by the CyberBrick system proportional inputs, the bad the relatively high noise and center hysterisis, which also explains, why stock code uses such a large deadzone value of 200 (code segments here, here and here) or even 500.

The default state of digital inputs K is high (=1), when actuated/pressed/shorted, they turn to 0. Thus they are low-active.