Skip to content

Conversation

Copy link

Copilot AI commented Jan 20, 2026

Touch Sensor Support on Powered Up Platform

Changes Completed:

  • Add sensor_data field to struct _pbio_port_dcm_t in lib/pbio/src/port_dcm_pup.c
  • Uncomment line in pbio_port_dcm_thread() to read touch sensor value and store it
  • Update pbio_port_dcm_get_analog_value() to return dcm->sensor_data
  • Update pbio_port_dcm_assert_type_id() to handle touch sensor type
  • Add touch sensor handling in Python bindings in pybricks/iodevices/pb_type_iodevices_pupdevice.c
  • Build and test the changes with make -C bricks/technichub
  • Address code review feedback - use proper API abstraction

Implementation Details:

C Library Changes (lib/pbio/src/port_dcm_pup.c):

  • Added uint32_t sensor_data field to struct _pbio_port_dcm_t to store touch sensor readings
  • Modified pbio_port_dcm_thread() to read touch sensor value from GPIO pin p5 and store it in dcm->sensor_data
  • Updated pbio_port_dcm_get_analog_value() to return the stored sensor_data value
  • Added LEGO_DEVICE_TYPE_ID_LPF2_TOUCH case to pbio_port_dcm_assert_type_id() to properly detect touch sensors

Python Bindings (pybricks/iodevices/pb_type_iodevices_pupdevice.c):

  • Added passive_port field to iodevices_PUPDevice_obj_t to store port reference for passive devices
  • Updated init_passive_pup_device() to check for touch sensor using pbio_port_get_analog_value() (proper abstraction)
  • Modified iodevices_PUPDevice_read() to handle touch sensor reads by calling pbio_port_get_analog_value()
  • Touch sensor read returns a boolean value indicating pressed state

Build Status:

✅ Successfully builds for technichub platform

Original prompt

This section details on the original issue you should resolve

<issue_title>Touch sensor support on Powered Up platform</issue_title>
<issue_description>There are not any official LEGO touch sensors for Powered Up. However, from reverse engineering, we have seen that it was at least considered at some point. We can take advantage of this to allow people to make their own 3rd-party touch sensor.

Here is how we could do it:

The easy parts are that we need read data from the sensor. We just need to uncomment the last line here (from lib/pbio/src/port_dcm_pup.c):

            // ID1 is inverse of touch sensor value
            // TODO: save this value to sensor dcm
            // sensor_data = !pbdrv_gpio_input(&pins->p5);

And we need a way to get the data in Python which can be done with the following change:

diff --git a/pybricks/iodevices/pb_type_iodevices_pupdevice.c b/pybricks/iodevices/pb_type_iodevices_pupdevice.c
index f0da7c452..bb114b9a1 100644
--- a/pybricks/iodevices/pb_type_iodevices_pupdevice.c
+++ b/pybricks/iodevices/pb_type_iodevices_pupdevice.c
@@ -164,6 +164,11 @@ static mp_obj_t iodevices_PUPDevice_read(size_t n_args, const mp_obj_t *pos_args
         iodevices_PUPDevice_obj_t, self,
         PB_ARG_REQUIRED(mode));
 
+    if (self->passive_id == LEGO_DEVICE_TYPE_ID_LPF2_TOUCH) {
+        // TODO: replace `sensor_data` with actual reading from the touch sensor
+        return mp_obj_new_bool(sensor_data);
+    }
+
     // Passive devices don't support reading.
     if (self->passive_id != LEGO_DEVICE_TYPE_ID_LPF2_UNKNOWN_UART) {
         pb_assert(PBIO_ERROR_INVALID_OP);

The tricky part is that sensor_data is not defined and we need to figure out the correct way to pass data from one module to the other.

For example, we could add a uint32_t sensor_data; field to

struct _pbio_port_dcm_t {
    /** Most recent one-off device ID candidate. */
    lego_device_type_id_t type_id;
    /** Previous one-off device ID candidate. */
    lego_device_type_id_t prev_type_id;
    /** Number of consecutive detections of the same device ID. */
    uint8_t dev_id_match_count;
    // Intermediate state values to preserve in between async calls.
    dev_id_group_t dev_id1_group;
    uint8_t gpio_value;
    uint8_t prev_gpio_value;
};

Then modify this function to return dcm->sensor_data;.

uint32_t pbio_port_dcm_get_analog_value(pbio_port_dcm_t *dcm, const pbdrv_ioport_pins_t *pins, bool active) {
    return 0;
}

And in iodevices_PUPDevice_read() replace sensor_data with a call to pbio_port_get_analog_value() to get that value.

Originally posted by @dlech in pybricks/support#2556 (reply in thread)</issue_description>

<agent_instructions>Use the information in the issue to make the required code changes. The code should compile with make -C bricks/technichub.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

- Added sensor_data field to pbio_port_dcm_t struct
- Implemented reading touch sensor value and storing it
- Updated pbio_port_dcm_get_analog_value to return sensor_data
- Added touch sensor handling in pbio_port_dcm_assert_type_id
- Added touch sensor support in PUPDevice Python bindings
- Build tested successfully on technichub

Co-authored-by: dlech <963645+dlech@users.noreply.github.com>
Copilot AI changed the title [WIP] Add touch sensor support on Powered Up platform Add touch sensor support for Powered Up platform Jan 20, 2026
Copilot AI requested a review from dlech January 20, 2026 21:20
@coveralls
Copy link

coveralls commented Jan 20, 2026

Coverage Status

coverage: 49.848% (-0.02%) from 49.87%
when pulling c429735 on copilot/add-touch-sensor-support
into 5df8453 on master.

@dlech dlech marked this pull request as ready for review January 20, 2026 22:12
@dlech dlech requested a review from laurensvalk January 20, 2026 22:12
@dlech
Copy link
Member

dlech commented Jan 20, 2026

Wow, it actually worked on the first try!

from pybricks.iodevices import PUPDevice
from pybricks.parameters import Port
from pybricks.tools import wait

device = PUPDevice(Port.B)

# Should print 5 for touch sensor
print(device.info())

while True:
    # prints True when pressed, otherwise False
    print(device.read(0))
    wait(500)
{'id': 5}
False
...
False
True
False
False
True
True
True
True
False
True
True
True
True
False
...

@dlech
Copy link
Member

dlech commented Jan 20, 2026

Required wiring:

  • Connect smaller resistor (e.g >= 220 Ω) between pin 5 (ID1) and pin 6 (ID2) for proper ID detection.
  • Connect bigger resistor (e.g. 10kΩ) in series with touch sensor between pins 5 (ID1) and 3 (GND).

lego_device_type_id_t type_id = LEGO_DEVICE_TYPE_ID_ANY_DC_MOTOR;
// Check for touch sensor first.
lego_device_type_id_t type_id = LEGO_DEVICE_TYPE_ID_LPF2_TOUCH;
pbio_error_t err = pbio_port_dcm_assert_type_id(pbio_port_dcm_init_instance(port_id - PBIO_PORT_ID_A), &type_id);
Copy link
Member

@laurensvalk laurensvalk Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ew 😄

Could assert getting an analog value, which will assert the type without resetting the dcm.

pbio_port_get_analog_value(pbio_port_t *port, lego_device_type_id_t type_id, bool active, uint32_t *value)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c429735. Now using pbio_port_get_analog_value() instead of directly accessing the dcm.

@laurensvalk
Copy link
Member

Impressive! Heh, I guess we have fairly good abstractions. 🙈

I've been wondering if we should have an iodevices.GPIODevice on all platforms, but I suppose an auto-detectable LPF2 sensor is nice. read(0) does ignore the 0 here so it is a little out of place, but then again so is the dc motor stuff on this class.

@dlech
Copy link
Member

dlech commented Jan 21, 2026

@copilot fix this comment and ignore all other comments.

Replaced direct dcm access with proper abstraction layer as suggested
in code review. This avoids resetting the dcm and uses the intended API.

Co-authored-by: dlech <963645+dlech@users.noreply.github.com>
Copy link
Author

Copilot AI commented Jan 21, 2026

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • esm.ubuntu.com
    • Triggering command: /usr/lib/apt/methods/https /usr/lib/apt/methods/https (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@dlech dlech requested a review from laurensvalk January 21, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Touch sensor support on Powered Up platform

4 participants