Max data transfer size/rate via USART? #4412
Replies: 1 comment
-
Posted at 2017-03-25 by oesterle Can you post your source?
Next, sending chunks on an interval would probably work, but isn't the most efficient or robust, since you are depending on the receiving Pico always being ready on time for the next chunk. Which it might not always: sometimes it may have another task on its plate. A somewhat more robust, brute force method, is to do your own chunking. Instead of waiting a fixed interval between chunks, you send a chunk, then wait for the receiving device to send you an acknowledgment. Then you send the next chunk, etc. Before all this, you send something like Posted at 2017-03-26 by jtq Thanks @oesterle - some great suggestions there, particularly investigating Serial1.pipe(). I've veered away from writing my own solution so far, because this feels like something I'd expect to be handled internally by the Espruino runtime (at least some way to read the current state of the Serial buffer, so you know when it's safe to push more data into the connection)... but if there's nothing then I guess it's a straight choice between either creating a pull request into the Espruino runtime or write my own JS routines to manage it. The basic setup is pretty simple - just
to send a data chunk down it. For reference, the data object I'm sending includes a few KB of base64 image and audio data, and going over a couple of KB total is about the point it blows up the connection and crashes the chip. Then the same setup line on the receiving side, and
to receive and process it. Edit: Ah- looks like Posted at 2017-03-27 by jtq ... Right. I think I've finally tracked down where the Serial buffer size is defined in the Espruino source code. I'm leaving this step-by-step trail of breadcrumbs here in case it helps anyone else to decode the Espruino source code, and to refresh my memory if I ever need to track down chip details like this again in the future. 1. First, we know we're looking for something to do with the Serial1 port. Serial1 is an instance of the Serial class, and (as per the API documentation) that's a USART.
So this is basically saying "get a single character from the
Phew! So long story short it looks like the size of the Serial output buffer is defined at compile time based on the RAM size of the chip. 18. Tracking back up the file we find that 19. Ahah - thank god for comments:
So let's take a look in the boards folder and see if we can spot anything that looks like our chip. 20. I don't know about you, but I have a good feeling about STM32F401CDISCOVERY.py for an STM32F401CDU6 chip, so let's look in there. 21. Booyah! I spy a That makes our output buffer for serial data a surprisingly-small 128 unsigned chars big. That seems way too small for something that only starts choking on writes of a couple of K or more, so perhaps there's another buffer somewhere, further up the stack that I've missed. Posted at 2017-03-27 by @gfwilliams Hi - sorry I'm a bit late to reply here. You really did have a good look in to it! The output buffer is relatively small, yes - but it won't cause a crash or any kind of problem if that is overflowed - the function will just take a longer time to execute as it will wait for the buffer to become non-full. For example:
Do you have up to date firmware? Does pressing Ctrl-C break out of it? Please could you try and post up a really simple bit of code that crashes the Pico? Something I could just stick in the RHS of the IDE and try myself? The only thing that immediately sticks out with what you're doing is:
Adding a character on to the end of strRepresentation will cause a new variable to be created. If strRepresentation really is big then it could use up all your memory (but it still shouldn't crash - it should warn you of low memory). To get around it you'd be better off doing 2 prints:
Posted at 2017-03-27 by @gfwilliams Just to add: on the receive side, you can check Posted at 2017-03-28 by jtq Hi @gfwilliams - thanks for your pointers. To answer your questions:
I took on board what you said about implicit redeclarations/copies of variables (dammit - too long writing JS and too many years since I wrote any C...), so I tried replacing the single concatenation-plus-print line with two separate prints instead... and suddenly the sending Pico seems to be pretty consistently completing the transfer without crashing, so perhaps my problem was indeed a memory issue caused by marshalling the data in memory, rather than a problem punting the data through USART as I suspected (now my receiving Pico's crashing instead, but I suspect it's probably a similar RAM-based problem on that end, too - needs further investigating). I didn't see any OOM error messages anywhere and the issue definitely wasn't handled "gracefully" in the REPL/Web IDE, so I'll keep investigating with my old (crashy) code, and I'll see if I can produce a minimal test-case for you in the IDE that reliably crashes the Pico, even if it does just turn out to be an OOM problem. Thanks for the pointer about I'll keep investigating given what you've told me, and I'll report back anything of interest I find in this thread. Thanks again! Posted at 2017-03-28 by @gfwilliams Great - thanks! If you do get a repeatable test case I'd love to know - I try very hard to make sure that Espruino never crashes and becomes totally unresponsive, so if it is a problem I'd like to try and get it sorted as soon as I can :) Posted at 2017-03-28 by jtq EDIT: I've removed this comment because it was misleading/incorrect. Posted at 2017-03-28 by jtq @gfwilliams: Ah - wait. It has nothing to do with the Serial connection after all - it's something to do with passing a variable through The simplest/most convenient test-code that replicates it is as follows - flash it to the Pico chip and run
The important factors here seem to be the following: The fact that If omit the concatenation I can send an arbitrarily large (20k+) messages without any problems. If I concatenate it with another string as per the code above, anything over 3680 characters causes the REPL to become unresponsive. That naively suggests to me it's not "just" an OOM problem in the JS code, as surely reserving/sending one 20k string is going to use more memory than even two or three implicit copies of a 3.6k one? The length of Assuming the concatenation as per the code above, the following boundaries seem to apply on the length of blockData: 4029 or more = REPL unresponsive, LED stays red (indicating JS processing is stopping immediately after 3681 - 4028 = REPL unresponsive, LED turns green after transmission finishes (so somethings killing the REPL, but the rest of the JS is executing correctly?) 3680 or fewer = LED turns green after transmission ends, REPL remains responsive after code finished executing I'm assuming that this is some sort of bug because from what you're saying I should be seeing errors thrown for OOM, and the REPL shouldn't stop responding under any circumstances. Is that any help? Posted at 2017-03-29 by @gfwilliams Yes, that's perfect - thanks! This looks like an internal interpreter problem then - but I'm amazingly surprised something like this has surfaced, since it would appear to be such a common thing to do. I'll take a look at this today and see if I can figure out what's wrong. Posted at 2017-03-29 by @gfwilliams Ok - good news! Looks like this was already fixed and will be in the 1v92 firmware. If you do 'advanced flash' from the Web IDE with this url: Sorry it's caused you so many troubles - I've been meaning to get 1v92 out for a while, but have been delayed a bit by wanting to get an issue with Puck.js and Windows HID fixed. Posted at 2017-03-29 by jtq That's awesome - thanks @gfwilliams! It didn't cause me much hassle in the end - it was more a useful learning experience. I'm just happy it was a legit bug and not something obvious I'd got wrong! Glad to hear it's already fixed in v1.92, and I can easily work around it for now now I know what causes it - as you pointed out it's actually better to avoid implicit memory allocations by concatenating vars anyway, so I'll just Thanks again - I just wanted to say I love the open ethos of Espruino (code, hadware, etc), and really appreciate how helpful and responsive you are to the community - even to an idiot newbie just getting started on embedded development. ;-) Posted at 2017-03-29 by oesterle @jtq, I see in your code that you're using Posted at 2017-03-29 by jtq Yeah - in my actual code I'm passing a complex JSON object that gets stringified for transmission over the serial port - the code above is a minimal test-case for Gordon, designed specifically to trigger the weird interpreter behaviour I'd noticed. The interpreter bug that causes the crash apparently depends only on how long the input to But you can legitimately pass a plain string (or any primitive JS value) into Posted at 2017-03-29 by oesterle Thank you for the link! Believe it or not, after 20+ years of JS I'm still learning new stuff. Didn't realize this, but you can also reformat/prettify JSON text by doing something like |
Beta Was this translation helpful? Give feedback.
-
Posted at 2017-03-25 by jtq
Background: I'm trying to transfer a stringified JSON object between two Picos (using Serial1, tx:B6, rx:B7 on each, shared GND, console shunted to USB, etc). It works for a couple of KB of data, but beyond a certain size of object the transfer appears to simply fail, leaving the sending Pico in an unresponsive state until it's power-cycled.
I'm guessing that there's some sort of internal buffer on the Serial port for when JS code delivers data faster than it can be transmitted, but I'm having trouble finding any information on how big it is, or strategies for robustly working around this issue (eg, chunking the data and sending a chunk at a time using setTimout/setInterval is an obvious possibility, but how quickly/slowly should I queue each new chunk up, and/or how do I know when the previous chunk has finished transmitting)?
Also, does anyone have any information on which baud rates the Pico supports for Serial connections? I'm experimenting to ease the problem by selecting higher baud rates to reduce the chance of buffer overrun, but it's a slow process when you have to connect/disconnect and re-flash two different Picos for every test...
Basically is there anywhere I can look in more detail regarding the USART specs on the Pico? I've even tried digging into the Espruino source code on Github, but I couldn't find any info there either.
Beta Was this translation helpful? Give feedback.
All reactions