-
-
Notifications
You must be signed in to change notification settings - Fork 334
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
Add an optional LibFTDI serial backend #218
Conversation
I like it, it looks great! I will add a test for it to CI pipelines and check that it works the same way |
src/utils/serial.cpp
Outdated
log_error ("read_from_serial_port"); | ||
return bytes_read; | ||
} | ||
next_bytes += bytes_read; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you need to check in a loop manually here? looks like there is timeout configured(ctx.set_usb_read_timeout (ms_timeout);
) it should handle this case I guess
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I dont fully understand this statement in while loop next_bytes == bytes_to_read
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for looking at these things !
Unfortunately it respects the timeout only for the usb bus. The ftdi chip will actually send an empty data packet after its configured "latency" delay expires, which the library treats as a response. See http://www.ftdichip.com/Support/Documents/AppNotes/AN232B-04_DataLatencyFlow.pdf page 8 item 4; there's also more information elsewhere in that document. Even if the usb timeout is set to 1 second, the chip will usually reply with its empty data buffer after only 16 ms.
next_bytes == bytes_to_read
is true when no data has been received yet. I can probably add a flag to make that clearer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"next_bytes == bytes_to_read is true when no data has been received yet. I can probably add a flag to make that clearer"
yep, I realise it. But should there be a check that size > 0 in this loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed another commit where the loop is recoded to clarify that it stops as soon as size > 0 . I think I hadn't made up my mind whether to do that, when I first coded it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh! yes there is an edge case if 0 is passed to the function. i'll try to address that in a second push.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean that in a loop you read bytes and decrease size variable by bytes read so loop should be stopped when its 0
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
at that point next_bytes == bytes_to_read
would have been false, stopping the loop (edit: unless size was passed as 0, which was the edge case). is the new loop structure clearer? it's intended to only performs 1 read that returns data. it could loop until size is read, doesn't seem to really matter.
src/utils/serial.cpp
Outdated
|
||
void log_error (const char *action) | ||
{ | ||
logger->error ("libftdi {}: {} -> {}", description, action, ctx.error_string ()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lets rm this logger completely or replace it by board_logger, this way logging behavior will be under user's control from high level api
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was actually trying to connect to board_logger here, see this line: https://github.com/brainflow-dev/brainflow/pull/218/files#diff-707d50ff0c1f53ceed4b00bbb44de221f4e4804adeb6c8a62059677a2f153933R39 . What is the right way to connect to board_logger ? Maybe add a constructor parameter and pass it in?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its a public static field in a board class, I guess you can use forward declaration or include header(maybe it will lead to circular includes).
Its preferred to use safe_logger
instead spdlog directly, its a non static method of board class. It should be possible to pass it as an argument in constructor
would be great to add a CI test for it, it will also help to understand which packages should be preinstalled in order to use this lib |
TODO for this PR:
|
https://github.com/brainflow-dev/brainflow/blob/master/.github/workflows/run_libftdi.yml#L35 lets change this to add libftdi checks to ci pipeline |
This lets it work out of the box on more setups.
Alternatives to this include moving safe_logger out of the board class, or throwing exceptions for errors.
@Andrey1994 I think I've implemented the two items. Here are some further thoughts:
|
"i don't see the purpose of skip_logs yet, given that board_logger is statically defined and should outlive destructors. I built brainflow with the safe_logger check disabled and ran into no associated issues in get_data_demo or CI. i'm inferring it's something inside spdlog?" loggers used inside release_session method and its fine. Destructor calls release_session method and its needed to handle the case if user didnt call release_session manually. spdlog has a known issue that it should not be called from destructors when program ends, some objects in spdlog released automatically before and it leads to a crash |
"i'm used to managing errors with exceptions rather than return codes in C++. not sure what the rest of the library is using, but i copied the other serial classes and used return codes." here exceptions are not used, this code is called from other languages and they can get return code from C\C++ but they can not handle exception in native code |
feel free to review this PR, I splitted it by different files |
* Update settings.xml * Refactor brainflow_boards and board classes and implement marker api (brainflow-dev#212) * add insert_marker method Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * bump julia version Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * docs update Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * CI for libFTDI (brainflow-dev#219) * add template for libftdi ci job Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * Update run_libftdi.yml * hotfix for markers on oymotion Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * bump julia version Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * disable fascia tests temporary Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * adding opencollective to sponsor button Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * Add an optional LibFTDI serial backend (brainflow-dev#218) * Add an optional LibFTDI1++ serial backend * fixing marker type (brainflow-dev#220) * fix marker type Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * bump julia version Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * refactor osserial and libftdiserial (brainflow-dev#221) * refactor osserial and libftdiserial Signed-off-by: Andrey Parfenov <a1994ndrey@gmail.com> * hacked libftdi into android ci Co-authored-by: Andrey Parfenov <a1994ndrey@gmail.com>
Andrey1994 do you have any link to this issue? I didn't manage to find it at the spdlog repository. It could be the same issue of static initialisation/destruction order. EDIT: Andrey answered in discord. Two links were gabime/spdlog#1738 and |
This adds an alternative serial backend using libftdi. The backend is enabled by the cmake flag
USE_LIBFTDIPP
. libftdi itself needs to be built with itsFTDIPP
flag, to enable C++ bindings. It would be a simple change to the code to use the C library without the C++ bindings, if that's valued (EDIT: this has been done, and configuration flags to libftdi are no longer required. The new cmake flag is justUSE_LIBFTDI
).libftdi provides an alternative way to reference device paths. Instead of specifying the serial port as
/dev/ttyUSB0
, you can for example specifyi:0x0403:0x6015:0
which will select the first openbci dongle on the usb bus, ors:0x0403:0x6015:DM03H8Q9
which will select the openbci dongle with the serial number "DM03H8Q9".My goal in submitting this PR is to just open possible avenues towards implementing serial access on android (#216). Libftdi uses libusb under the hood, and libusb has some support for android already.
I understand changing the backends is not the preferred approach, but I am sharing this in case it is of interest. @Andrey1994 what are your thoughts?