8000 esp8266/uart: rework uart io structure so it can work with dupterm() and be used to disable stdio by mhoffma · Pull Request #2891 · micropython/micropython · GitHub
[go: up one dir, main page]

Skip to content

esp8266/uart: rework uart io structure so it can work with dupterm() and be used to disable stdio #2891

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from

Conversation

mhoffma
Copy link
Contributor
@mhoffma mhoffma commented Feb 21, 2017

This is the start in a series of refactorings of the UART on esp8266 so we can move in the direction of a single slotted. The first in the series of commits will globalize the UART py structure so that we have direct access to the py object from the root state structure. The original dialogue #2814 which discusses a hack implementation that allowed for disabling stdio on the UART for boards which only have a single serial port.

@mhoffma
Copy link
Contributor Author
mhoffma commented Feb 21, 2017

How does the group feel about moving input_buf into the py UART structure, the ask is if its reasonable direction to take

typedef struct _pyb_uart_obj_t {
    mp_obj_base_t base;
    uint8_t uart_id;
    uint8_t bits;
    uint8_t parity;
    uint8_t stop;
    uint32_t baudrate;
    uint16_t timeout;       // timeout waiting for first char (in ms)
    uint16_t timeout_char;  // timeout waiting between chars (in ms)
    ringbuf_t input_buf;
    byte input_buf_array[256];
} pyb_uart_obj_t;

@mhoffma mhoffma closed this Feb 21, 2017
@mhoffma mhoffma reopened this Feb 21, 2017
@dpgeorge
Copy link
Member

How does the group feel about moving input_buf into the py UART structure,

The input buffer for the REPL should be separate from the UART (so webrepl can work without uart). The uart itself can have an rx buffer, but that should ideally be dynamically allocated so memory is not wasted if the buffer is not used. And I think the esp8266 has a large-ish internal uart buffer so one might get away without a uart rx buffer at all.

Copy link
Contributor Author
@mhoffma mhoffma left a comment

Choose a reason for hiding this comment

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

using the hardware structure for uart_rx_any doesn't actually work, we have pulled the samples out by the time we get here. I think input_buf actually needs to be part of the low-level uart code and not spread out.

@mhoffma
Copy link
Contributor Author
mhoffma commented Feb 22, 2017

couple of subtle points not to get lost:

  1. I believe the uart needs to be separated out, so we can use it with gdbstub and mp.
  2. The uart requires an additional buffer because if we don't drain the RX buf in the ISR the interrupt will keep tripping, this is an observation I made during the above sequence of changes.
  3. The first attempt at nostdio didn't actually work so its good you @dpgeorge pointed out that it was hacky everything was being multiplexed into that input_buf both streams and it was just coincidental that it appeared to work.

So I think REPL needs an input_buf, with its signaling functions that input has arrived using of course the same naming scheme. And the uart or what ever else needs to be responsible for buffering its own input and causing the async signaling to occur.

@mhoffma
Copy link
Contributor Author
mhoffma commented Feb 23, 2017

picture is work in progress

mprx

with this series of patches the following behavior exists. Current design works as is functionality new functionality is predicated on term_obj being set via dupterm. Once the dupterm has been configured the serial port is disabled and can be provisioned to other functions! nostdio over uart folded into the dupterm infrastructure.

connecting uart to repl via dupterm works as expected.

>>> import os
>>> os.dupterm()
>>> import machine
>>> u=machine.UART(0)
>>> u
UART(0, baudrate=115200, bits=8, parity=None, stop=1, timeout=0, timeout_char=1)
>>> os.dupterm(u)
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> os.dupterm()
UART(0, baudrate=115200, bits=8, parity=None, stop=1, timeout=0, timeout_char=1)
>>> 

I tested webrepl and my little telnet server and things seem to function well reading and writing files etc. I'm going to add another field to UART for the buffer as suggested and make the default 16 for UART0 and 0 for UART1 which is only valid for TX anyways.

@mhoffma
Copy link
Contributor Author
mhoffma commented Feb 23, 2017

repldemo

@mhoffma mhoffma changed the title esp8266/uart: hardwire references to the uarts in ROOT esp8266/uart: rework uart io structure so it can work with dupterm() and be used to disable stdio Feb 23, 2017
@dpgeorge dpgeorge mentioned this pull request Apr 4, 2017
@svily0
Copy link
svily0 commented Apr 4, 2017

Well, I actually started from here.
The board still reboots when data from my acquisition device contains 0x03 in it. Hence the patch.

svily0 referenced this pull request in svily0/micropython Apr 5, 2017
Revert part of previus commit
@mohpor
Copy link
mohpor commented Apr 18, 2017

@mhoffma
Hi, Thanks for this PR.
I'm very much interested in the implementation of this feature.
That being said, I don't have enough knowledge nor time (at the moment) to help you further this cause, but I defintely need this feature in the near future and I can help test this.
Now, I have successfully deployed your fork onto my ESP-12F, everything appears to be ok but I have a problem which appears to be related to UART's buffer. When I try to read from UART, I can't get the full extent of expected values, but only 15 characters. To clear my comment up, I'm using the UART to send AT comands to another module , I know the expected responses my other module would send (through both documentation and direct read from its UART in a separate config). When I send AT\r\n I can successfully read OK\r, no problems, but when I send another AT command which potentially returns a value of multiple lines, I can read only 15 characters! it seems like UART doesn't buffer more than that.

Is my conclusion coreect?
Am I doing something wrong?
How can I fix this situation?

P.S.: Using main branch of the upstream Micropython repo, when I send my AT commands, I can see the full response getting back from my module (which will blow back to my REPL and causes mass confusion!)

@craftyguy
Copy link
craftyguy commented May 20, 2017

@mohpor

I suspect this part of the patch is causing you to only receive 15 characters (technically, 15 printables + a \0 ?)

if (self->uart_id == 0 && self->rxbuflen < 16)
    self->rxbuflen=16;        /* probably too restrictive */

I don't know why @mhoffma is so restrictive in selecting a buffer of this size.. perhaps he will inform us!

@craftyguy
Copy link

@mohpor
So, after a little more inspection, it seems like you can define a rxbuflen parameter when initializing the machine.UART object. If you don't, it defaults to 16 characters.

@craftyguy
Copy link

@dpgeorge What open concerns do you have with this PR that are preventing it from being merged? Not OP, but definitely interested in this!

@dpgeorge
Copy link
Member

What open concerns do you have with this PR that are preventing it from being merged?

In general it looks OK, but since it makes some deep changes wrt UART processing it needs a thorough review. It will need to wait until after v1.9. There are also some code style issues, such as missing curly braces for single-line statement blocks, and missing spaces around operators, like =.

@dpgeorge
Copy link
Member
dpgeorge commented Jun 1, 2017

@mhoffma are you still interested in working on this? As said above there are lots of code style problems. If you don't have time then I will take it from here.

@mhoffma
Copy link
Contributor Author
mhoffma commented Jun 1, 2017 via email

void mp_hal_uart_rx_intr(int uart_no) {
int ch;
mp_obj_t term = MP_STATE_PORT(term_obj);
bool uart_term=(term == NULL || term == MP_STATE_PORT(pyb_uart_objs)[uart_no]);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

should this be

bool uart_term = (term == NULL || term == MP_STATE_PORT(pyb_uart_objs)[uart_no]);

bool uart_term=(term == NULL || term == MP_STATE_PORT(pyb_uart_objs)[uart_no]);

for (;;) {
if ((ch = uart_rx_one_char(uart_no))==-1)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

is this the correct spacing?

if ((ch = uart_rx_one_char(uart_no)) == -1)

@@ -146,6 +149,31 @@ void mp_hal_signal_input(void) {
#endif
}

void mp_hal_uart_rx_intr(int uart_no) {
Copy link
Member

Choose a reason for hiding this comment

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

IMO this belongs in machine_uart.c, since it's specific to UART behaviour. Also, since UART1 can't receive it doesn't need to take an argument.

if (ch == mp_interrupt_char)
mp_keyboard_interrupt();
else
ringbuf_put(&input_buf, ch);
Copy link
Member

Choose a reason for hiding this comment

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

Please use curly braces for single-line bodies.

@@ -40,6 +40,10 @@ extern const struct _mp_print_t mp_debug_print;
extern ringbuf_t input_buf;
// Call this after putting data to input_buf
void mp_hal_signal_input(void);
// Call this to put characters into connected uart buffer if repl connected
void mp_hal_uart_rx_intr(int uart_no);
Copy link
Member

Choose a reason for hiding this comment

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

The way this function is currently implemented, it handles chars even if REPL is not connected.

#include "modmachine.h"

// UartDev is defined and initialized in rom code.
extern UartDevice UartDev;

#define RXBUF_SIZE 16
Copy link
Member

Choose a reason for hiding this comment

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

This is unused.

@@ -73,6 +89,7 @@ STATIC void pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const mp_o
//{ MP_QSTR_rx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
{ MP_QSTR_rxbuflen, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
Copy link
Member

Choose a reason for hiding this comment

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

Use 16 instead of 0 for the default.

@@ -155,6 +172,10 @@ STATIC void pyb_uart_init_helper(pyb_uart_obj_t *self, size_t n_args, const mp_o
self->timeout_char = min_timeout_char;
}

self->rxbuflen = args[ARG_rxbuflen].u_int;
if (self->uart_id == 0 && self->rxbuflen < 16) {
Copy link
Member

Choose a reason for hiding this comment

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

Don't need this check if 16 is given as a default above.

@@ -211,6 +244,29 @@ STATIC const mp_rom_map_elem_t pyb_uart_locals_dict_table[] = {

STATIC MP_DEFINE_CONST_DICT(pyb_uart_locals_dict, pyb_uart_locals_dict_table);



Copy link
Member

Choose a reason for hiding this comment

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

Please don't add blank lines like this. Follow existing conventions.

pyb_uart_init_helper(self, n_args - 1, args + 1, &kw_args);

if (self->rxbuflen) {
if (self->buf) {
Copy link
Member

Choose a reason for hiding this comment

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

Please use 4 spaces for indenting.

@dpgeorge
Copy link
Member
dpgeorge commented Jun 2, 2017

To move forward with this we really need to decide if the dupterm (on esp8266) will have only 1 slot, or more than one. This PR goes on the basis of 1 slot only. That will mean that (eventually, doesn't need to be address right now by this PR):

  • uos.dupterm(machine.UART(0, 115200)) should be added to boot.py so the REPL appears by default on the UART.
  • uos.dupterm(None) can be used to completely disable the REPL. For example, this is used in cases where the UART is freed for other use and there's no WebREPL (normal case for a stand-alone device doing its thing). In particular this means that a NULL dupterm object does not mean to use the default UART, as this PR currently assumes.
  • webrepl needs to be modified so that it restores the UART once it is closed/deactivated. But it should only restore it if the UART was there before webrepl was activated.

On the other hand, if dupterm has 2 slots then it can behave as:

  • uos.dupterm(machine.UART(0, 115200), 1) can be added to boot.py to put UART in slot 1 (the second slot).
  • uos.dupterm(None, 1) can be used to disable UART.
  • webrepl would do uos.dupterm(ws) to enable itself, and uos.dupterm(None) to disable, which would work by having slot 0 as the default (equivalently one could write uos.dupterm(ws, 0) and uos.dupterm(None, 0)). webrepl doesn't need to know if UART is enabled or not.

For ports that want multiple dupterm slots it would be good to specify the above behaviour (or something similar) in the docs. For esp8266 it's then a question of whether it has the resources to implement 2 slots.

@mhoffma
Copy link
Contributor Author
mhoffma commented Jun 2, 2017 via email

@mhoffma
Copy link
Contributor Author
mhoffma commented Jun 2, 2017

Back on Feb13 we talked about the multiple slot stuff with @pfalcon on thread #2814 I think we came to the suggestion of keep things simple.

@pfalcon
Copy link
Contributor
pfalcon commented Jun 3, 2017

uos.dupterm(machine.UART(0, 115200)) should be added to boot.py so the REPL appears by default on the UART.

Yes, that's how it's intended to be. (Then people will throw away that line and cry "where's my repl???777".)

On the other hand, if dupterm has 2 slots then it can behave as

Yes, I guess let's do it that way. My concern, as before, is that this "explicit slot numbers" are somewhat adhoc. The situation is as with not allowing to mount a filesystem on root. But mounting is very user-visible feature, and for dupterm(), I guess we can go for compromise.

@pfalcon
Copy link
Contributor
pfalcon commented Jun 3, 2017

I'm confused on multiple slots (2), I think you can build that behavior right in Python keeping the kernel stuff as simple as possible.

I'm not sure what exactly you mean, but we discussed that in the ticket you linked to. Summary: a) @dpgeorge wants to provide "duplicate", not "switch" behavior out of the box; b) it's one thing to be able to potentially implement in Python, another to make it actually work reliably (with low memory, etc.).

@mhoffma
Copy link
Contributor Author
mhoffma commented Jun 7, 2017

That's fine we can do it anyway you want of course. Is it possible that we add that behavior on a separate PR, adding this behavior in two stages along the lines of an incremental fashion?

dmkent added a commit to dmkent/micropython that referenced this pull request Aug 16, 2017
From @dpgeorge on micropython#2891:

    IMO this belongs in machine_uart.c, since it's specific to UART
    behaviour. Also, since UART1 can't receive it doesn't need to
    take an argument.

Makes more sense here as it is UART specific.
@dpgeorge
Copy link
Member

Ability to disbale UART(0) on the REPL was implemented in afd0701

@dpgeorge dpgeorge closed this May 21, 2018
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.

6 participants
0