Skip to content

Commit 286f2e0

Browse files
committed
pybricks.tools: Add read_input_byte() function.
This adds a new non-blocking function to read one byte from stdin. This is needed since there is no other way to read from stdin on the Move hub. It is also useful on other hubs as it eliminates the need to use poll() to avoid blocking reads. Fixes: pybricks/support#1102
1 parent 04a4173 commit 286f2e0

File tree

7 files changed

+79
-2
lines changed

7 files changed

+79
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Added
88
- Added `'modes'` entry to the dictionary returned by `PUPDevice.info()`. It
99
is a tuple of `(name, num_values, data_type)` tuples for each available mode.
10+
- Added `pybricks.tools.read_input_byte()` function ([support#1102]).
1011

1112
### Changed
1213
- Changed internal drivers for LEGO devices (motors and sensors) on all platforms.
@@ -17,6 +18,7 @@
1718

1819
[support#1095]: https://github.com/pybricks/support/issues/1095
1920
[support#1098]: https://github.com/pybricks/support/issues/1098
21+
[support#1102]: https://github.com/pybricks/support/issues/1102
2022

2123
## [3.3.0b6] - 2023-06-02
2224

bricks/ev3dev/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,9 @@ test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(GRX_TEST_PLUGIN_LIB)
311311
GRX_PLUGIN_PATH=$(realpath $(BUILD)) GRX_DRIVER=test \
312312
./run-tests.py
313313

314+
repl: $(BUILD)/$(PROG) $(GRX_TEST_PLUGIN_LIB) modules/core.py
315+
GRX_PLUGIN_PATH=$(realpath $(BUILD)) GRX_DRIVER=test $< -i modules/core.py
316+
314317
# Value of configure's --host= option (required for cross-compilation).
315318
# Deduce it from CROSS_COMPILE by default, but can be overridden.
316319
ifneq ($(CROSS_COMPILE),)

bricks/ev3dev/ev3dev_mphal.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@
2626

2727
#include <assert.h>
2828
#include <errno.h>
29+
#include <poll.h>
2930
#include <time.h>
3031
#include <sys/time.h>
32+
#include <unistd.h>
3133

3234
#include "py/mphal.h"
3335
#include "py/runtime.h"
36+
#include "py/stream.h"
3437

3538
void mp_hal_stdout_tx_flush(void) {
3639
// currently not buffered
@@ -55,3 +58,26 @@ void pb_ev3dev_hal_delay_ms(mp_uint_t ms) {
5558
break;
5659
}
5760
}
61+
62+
uintptr_t mp_hal_stdio_poll(uintptr_t flags) {
63+
struct pollfd fds[] = {
64+
{ .fd = STDIN_FILENO, .events = flags & MP_STREAM_POLL_RD ? POLLIN : 0, },
65+
{ .fd = STDOUT_FILENO, .events = flags & MP_STREAM_POLL_WR ? POLLOUT : 0, },
66+
};
67+
int ret;
68+
69+
MP_HAL_RETRY_SYSCALL(ret, poll(fds, MP_ARRAY_SIZE(fds), 0), mp_raise_OSError(err));
70+
71+
uintptr_t rflags = 0;
72+
73+
if (ret > 0) {
74+
if (fds[0].revents & POLLIN) {
75+
rflags |= MP_STREAM_POLL_RD;
76+
}
77+
if (fds[1].revents & POLLOUT) {
78+
rflags |= MP_STREAM_POLL_WR;
79+
}
80+
}
81+
82+
return rflags;
83+
}

bricks/ev3dev/modules/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
GyroSensor,
1010
)
1111
from pybricks.parameters import Port, Stop, Direction, Button, Color
12-
from pybricks.tools import wait, StopWatch, DataLog
12+
from pybricks.tools import read_input_byte, wait, StopWatch, DataLog
1313
from pybricks.robotics import DriveBase
1414
from pybricks.media.ev3dev import ImageFile, SoundFile
1515

bricks/virtualhub/mp_port.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
// MicroPython port-specific implementation hooks
55

6+
#include <poll.h>
67
#include <pthread.h>
78
#include <signal.h>
89
#include <stdbool.h>
910
#include <stdio.h>
1011
#include <stdlib.h>
1112
#include <sys/select.h>
13+
#include <unistd.h>
1214

1315
#include <contiki.h>
1416

@@ -25,6 +27,7 @@
2527
#include "py/objstr.h"
2628
#include "py/objtuple.h"
2729
#include "py/runtime.h"
30+
#include "py/stream.h"
2831

2932
#include "pybricks/util_pb/pb_error.h"
3033
#include <pybricks/common.h>
@@ -140,3 +143,26 @@ void pb_virtualhub_delay_us(mp_uint_t us) {
140143
pb_virtualhub_poll();
141144
}
142145
}
146+
147+
uintptr_t mp_hal_stdio_poll(uintptr_t flags) {
148+
struct pollfd fds[] = {
149+
{ .fd = STDIN_FILENO, .events = flags & MP_STREAM_POLL_RD ? POLLIN : 0, },
150+
{ .fd = STDOUT_FILENO, .events = flags & MP_STREAM_POLL_WR ? POLLOUT : 0, },
151+
};
152+
int ret;
153+
154+
MP_HAL_RETRY_SYSCALL(ret, poll(fds, MP_ARRAY_SIZE(fds), 0), mp_raise_OSError(err));
155+
156+
uintptr_t rflags = 0;
157+
158+
if (ret > 0) {
159+
if (fds[0].revents & POLLIN) {
160+
rflags |= MP_STREAM_POLL_RD;
161+
}
162+
if (fds[1].revents & POLLOUT) {
163+
rflags |= MP_STREAM_POLL_WR;
164+
}
165+
}
166+
167+
return rflags;
168+
}

bricks/virtualhub/mpvarianthal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
void mp_hal_set_interrupt_char(char c);
3434

35-
#define mp_hal_stdio_poll unused // this is not implemented, nor needed
35+
uintptr_t mp_hal_stdio_poll(uintptr_t);
3636
void mp_hal_stdio_mode_raw(void);
3737
void mp_hal_stdio_mode_orig(void);
3838

pybricks/tools/pb_module_tools.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "py/mphal.h"
1111
#include "py/objmodule.h"
1212
#include "py/runtime.h"
13+
#include "py/stream.h"
1314

1415
#include <pbio/int_math.h>
1516

@@ -83,6 +84,24 @@ STATIC mp_obj_t pb_module_tools_wait(size_t n_args, const mp_obj_t *pos_args, mp
8384
}
8485
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pb_module_tools_wait_obj, 0, pb_module_tools_wait);
8586

87+
/**
88+
* Reads one byte from stdin without blocking.
89+
*
90+
* @returns The integer value of the byte read or @c None if no data is available.
91+
*/
92+
STATIC mp_obj_t pb_module_tools_read_input_byte(void) {
93+
if (!(mp_hal_stdio_poll(MP_STREAM_POLL_RD) & MP_STREAM_POLL_RD)) {
94+
// No bytes available.
95+
return mp_const_none;
96+
}
97+
98+
// REVISIT: In theory, this should not block if mp_hal_stdio_poll() and
99+
// mp_hal_stdin_rx_chr() are implemented correctly and nothing happens
100+
// in a thread/interrupt/kernel that changes the state.
101+
return MP_OBJ_NEW_SMALL_INT(mp_hal_stdin_rx_chr());
102+
}
103+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(pb_module_tools_read_input_byte_obj, pb_module_tools_read_input_byte);
104+
86105
STATIC mp_obj_t pb_module_tools_run_task(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
87106
PB_PARSE_ARGS_FUNCTION(n_args, pos_args, kw_args,
88107
PB_ARG_REQUIRED(task),
@@ -127,6 +146,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pb_module_tools_run_task_obj, 1, pb_module_too
127146
STATIC const mp_rom_map_elem_t tools_globals_table[] = {
128147
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_tools) },
129148
{ MP_ROM_QSTR(MP_QSTR_wait), MP_ROM_PTR(&pb_module_tools_wait_obj) },
149+
{ MP_ROM_QSTR(MP_QSTR_read_input_byte), MP_ROM_PTR(&pb_module_tools_read_input_byte_obj) },
130150
{ MP_ROM_QSTR(MP_QSTR_run_task), MP_ROM_PTR(&pb_module_tools_run_task_obj) },
131151
{ MP_ROM_QSTR(MP_QSTR_StopWatch), MP_ROM_PTR(&pb_type_StopWatch) },
132152
{ MP_ROM_QSTR(MP_QSTR_multitask), MP_ROM_PTR(&pb_type_Task) },

0 commit comments

Comments
 (0)