Skip to content

Commit 737f452

Browse files
committed
pbio/drv/counter_ev3: Add hysteresis.
This reduces the likelihood of an unintended disconnection event if the NXT motor (without internal schmitt triggers) is in an in-between state. See pybricks/support#2536
1 parent 13d9793 commit 737f452

2 files changed

Lines changed: 25 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
### Fixed
88
- Fixed EV3 screen animation not closing on user display actions ([support#2585]).
99
- Fixed EV3 Touch Sensor not detected it is pressed while plugged in ([support#2589]).
10+
- Fixed EV3 Large and Medium Motors sometimes not detected ([support#2535]).
1011

12+
[support#2536]: https://github.com/pybricks/support/issues/2536
1113
[support#2585]: https://github.com/pybricks/support/issues/2585
1214
[support#2589]: https://github.com/pybricks/support/issues/2589
1315

lib/pbio/drv/counter/counter_ev3.c

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// SPDX-License-Identifier: MIT
2-
// Copyright (c) 2025 The Pybricks Authors
2+
// Copyright (c) 2025-2026 The Pybricks Authors
33
//
44
// P5/6 IRQ config based on the ev3ninja/osek project:
55
// SPDX-License-Identifier: MPL-1.0
@@ -123,43 +123,40 @@ pbio_error_t pbdrv_counter_get_dev(uint8_t id, pbdrv_counter_dev_t **dev) {
123123
#define ADC_EV3_LARGE_0 (32)
124124
#define ADC_EV3_LARGE_1 (917)
125125
#define ADC_NXT_LARGE_1 (1014)
126-
#define ADC_MINDSENSORS_GLIDEWHEEL_0 (0)
127-
#define ADC_MINDSENSORS_GLIDEWHEEL_1 (1023)
128-
129-
static bool adc_is_close(uint32_t adc, uint32_t reference) {
130-
uint32_t error = adc > reference ? adc - reference : reference - adc;
131-
return error <= 21;
132-
}
133126

134127
/**
135128
* Gets the LEGO device type ID for an EV3 motor based on the ADC value.
136129
*
137130
* Each motor has two values (low and high) depending on the quadrature encoder
138-
* state. The large motor is 4000 in the high state but in the low state it
139-
* is indistinguishable from the EV3 large motor.
131+
* state. The NXT motor in the high state is slightly different from the EV3
132+
* large motor but it is indistinguishable in the low state, so we treat them
133+
* the same.
140134
*
141135
* The original firmware uses a dynamic process to distinguish other non-motor
142136
* devices. This is not implemented here. It does not appear necessary for
143137
* motors.
144138
*/
145-
static lego_device_type_id_t pbdrv_counter_ev3_get_type(uint16_t adc) {
139+
static lego_device_type_id_t pbdrv_counter_ev3_get_type(uint16_t adc, lego_device_type_id_t type_now) {
146140

147-
if (adc == ADC_MINDSENSORS_GLIDEWHEEL_0 || adc == ADC_MINDSENSORS_GLIDEWHEEL_1) {
148-
return LEGO_DEVICE_TYPE_ID_EV3_LARGE_MOTOR;
149-
}
141+
uint32_t margin = 30;
150142

151-
if (adc_is_close(adc, ADC_EV3_MEDIUM_0) || adc_is_close(adc, ADC_EV3_MEDIUM_1)) {
152-
return LEGO_DEVICE_TYPE_ID_EV3_MEDIUM_MOTOR;
143+
// If currently connected, be strict about the exit condition, and don't
144+
// allow changing to another type.
145+
if (type_now != LEGO_DEVICE_TYPE_ID_NONE) {
146+
return adc > ADC_EV3_NONE - margin && adc < ADC_EV3_NONE + margin ?
147+
LEGO_DEVICE_TYPE_ID_NONE: type_now;
153148
}
154149

155-
if (adc_is_close(adc, ADC_EV3_LARGE_0) || adc_is_close(adc, ADC_EV3_LARGE_1) || adc_is_close(adc, ADC_NXT_LARGE_1)) {
156-
// We can only detect the difference between NXT and EV3 motors 50% of
157-
// the time, depending on the optical encoder state. So always return
158-
// the same type for consistency. Their parameters are relatively close.
159-
return LEGO_DEVICE_TYPE_ID_EV3_LARGE_MOTOR;
150+
// Otherwise, if not connected, be strict about the entry condition. This
151+
// providing hysteresis to reduce the likelihood of false transitions.
152+
if (adc > ADC_EV3_MEDIUM_0 + margin && adc < ADC_EV3_MEDIUM_1 - margin) {
153+
return LEGO_DEVICE_TYPE_ID_NONE;
160154
}
161155

162-
return LEGO_DEVICE_TYPE_ID_NONE;
156+
// A medium or large motor was connected. Find out which one.
157+
return adc < (ADC_EV3_MEDIUM_0 + ADC_EV3_LARGE_0) / 2 || adc > (ADC_EV3_MEDIUM_1 + ADC_EV3_LARGE_1) / 2 ?
158+
LEGO_DEVICE_TYPE_ID_EV3_LARGE_MOTOR :
159+
LEGO_DEVICE_TYPE_ID_EV3_MEDIUM_MOTOR;
163160
}
164161

165162
#define PBDRV_COUNTER_EV3_TYPE_LOOP_TIME (10)
@@ -173,7 +170,7 @@ static void pbdrv_counter_ev3_update_type(pbdrv_counter_dev_t *dev) {
173170
// Get type detected now.
174171
uint16_t adc = 0;
175172
pbdrv_adc_get_ch(dev->adc_channel, &adc);
176-
lego_device_type_id_t type_id = pbdrv_counter_ev3_get_type(adc);
173+
lego_device_type_id_t type_id = pbdrv_counter_ev3_get_type(adc, dev->stable_type_id);
177174

178175
// Update number of consecutive identical detections.
179176
if (dev->last_type_id == type_id && dev->position == dev->type_id_position) {
@@ -187,6 +184,9 @@ static void pbdrv_counter_ev3_update_type(pbdrv_counter_dev_t *dev) {
187184
// Update stable type if we have seen enough identical detections,
188185
// including none detections.
189186
if (dev->type_id_count >= PBDRV_COUNTER_EV3_TYPE_MIN_STABLE_COUNT) {
187+
if (dev->stable_type_id != type_id) {
188+
DEBUG_PRINT("Detected %d (was %d)\n", type_id, dev->stable_type_id);
189+
}
190190
dev->stable_type_id = type_id;
191191
}
192192
}
@@ -212,7 +212,6 @@ static pbio_error_t pbdrv_counter_device_detect_process_thread(pbio_os_state_t *
212212
PBIO_OS_ASYNC_END(PBIO_SUCCESS);
213213
}
214214

215-
216215
pbio_error_t pbdrv_counter_assert_type(pbdrv_counter_dev_t *dev, lego_device_type_id_t *expected_type_id) {
217216

218217
if (dev->stable_type_id == LEGO_DEVICE_TYPE_ID_NONE) {

0 commit comments

Comments
 (0)