drivers: initial commit of generic AT parser module#7084
drivers: initial commit of generic AT parser module#7084vincent-d merged 9 commits intoRIOT-OS:masterfrom
Conversation
|
I started to work on prototyping with our modem some weeks ago, so I gave a try to your code. It worked good once I understood I had to add a This is much more simpler that the system I used on a previous project which worked with a lot of callbacks. But here it's hard to deal with responses you do not expect. I'll give some more try! |
|
@miri64 Indeed. Thanks for pointing me to this. Cheers |
oh, I hoped I had found a combination of line endings to send / line endings to expect that works. The two modems I tried accept a line with just |
Very good point. I planned to add support for that later, once I come up with a process that works and doesn't suck. Suggestions welcome! |
It would be handy, indeed. I use a ublox modem, which needs a |
|
Any update here ? @kaspar030 |
aabadie
left a comment
There was a problem hiding this comment.
Maybe the device descriptor pointers could be made const when possible ?
|
I gave another try and proposed some improvements in kaspar030#27 |
drivers/at/at.c
Outdated
| return -1; | ||
| } | ||
|
|
||
| if (at_expect_bytes(dev, AT_END_OF_LINE "\r\n", sizeof(AT_END_OF_LINE) + 1, timeout)) { |
There was a problem hiding this comment.
shouldn't this be AT_END_OF_LINE "\n" as second parameter?
There was a problem hiding this comment.
On my modem (and I think it works the same for others as well), if you send AT\r you get as a response:
AT\r <- echo
\r\nOK\r\n
There was a problem hiding this comment.
maybe I missunderstand the line above, but AT_END_OF_LINE is
#define AT_END_OF_LINE "\r"So the line above does make a string concatenation, leading to \r\r\n, right?!
There was a problem hiding this comment.
And this is wanted? I don't quite see it, but if it is intended then never mind...
There was a problem hiding this comment.
Yes, the AT_END_OF_LINE is expected after the command, the \r\n before the first response line.
It seems that some modems can use different end of line character than \r, that's why it's configurable.
There was a problem hiding this comment.
but just to make sure: wouldn't it make sense then, to make the characters that are expected before the first response line also configurable?
There was a problem hiding this comment.
Found a minor typo.
Do you think it could be even more generalized to any string command controlled devices ? (not only AT). See #7505, where the device replies to commands like, sys reset\r\n or mac set deveui 0000000000000000\r\n with ok\r\n or invalid_param\r\n
drivers/include/at.h
Outdated
| * @param[in] len len of @p buffer | ||
| * @param[in] timeout timeout (in usec) | ||
| * | ||
| * @returns lenght of response on success |
There was a problem hiding this comment.
before I forget, there's a typo: s/lenght/length/. I saw it in other places.
There was a problem hiding this comment.
fixed two times int at.h.
|
@aabadie Sorry, I don't have a LoRaBee at hand. I do have sodaq ONE and ExpLoRer board with the same RN2483 device but unfortunately I can't connect my Atmel ICE to these boards (so I can't load RIOT programs). :-( |
drivers/include/at.h
Outdated
| * | ||
| * @param[in] dev device to operate on | ||
| * @param[in] bytes buffer containing bytes to expect | ||
| * @param[in] len number of bytes to expect |
There was a problem hiding this comment.
Why do we need 'len'? The length of 'bytes' should be leading, otherwise you need a sanity check in the function.
There was a problem hiding this comment.
I don't quite get the question. len specifies the nr of bytes in bytes. Maybe the docs are misleading?
There was a problem hiding this comment.
Why not rely on strlen(bytes) instead of having to pass a string and a length? In the implementation there is a while loop for 'len', but there is no sanity check whether there are that many chars in 'bytes'.
Without 'len' the usage could become as simple as:
if (at_expect_bytes(dev, AT_END_OF_LINE "\r\n", timeout)) {
Bonus: in this case getting rid of the confusing sizeof()+1
There was a problem hiding this comment.
Why not rely on strlen(bytes) instead of having to pass a string and a length?
the function is mostly used by other functions (quick grep counts five times). relying on strlen would lead to counting strings multiple times, for strings that are often static. having len as parameter allows the API user to decide whether to use strlen or not...
there is no sanity check whether there are that many chars in 'bytes'
There was a problem hiding this comment.
I'm not impressed by that argument.
You can change the while condition in at_expect_bytes() to
while (*bytes != 0) {
and then you won't need to do a strlen()
There was a problem hiding this comment.
That actually seems like a good idea. done.
|
|
||
| memset(resp_buf, 0, len); | ||
|
|
||
| while (len) { |
There was a problem hiding this comment.
What happens if the response string is longer than 'len' and the while loop is ended. If I understand this code well enough then I'm afraid there is no NUL byte terminating the string.
There was a problem hiding this comment.
That's what the memset in line 210 should take care of?
There was a problem hiding this comment.
No, not quite. Say there is a 'len' of 5 and on the input comes "abcdefg\r\n". The while loop ends as soon as "abcde" was read and resp_buf is completely filled up (no NUL byte anymore). No?
There was a problem hiding this comment.
Ah, right. I guess -ENOSPC would be the desired result in that case?
There was a problem hiding this comment.
Looking at the code again, if no full line could be read, the result of the function is -1. Shouldn't that be enough? Anyhow, I've added a condition setting the first byte of resp_buf to the null byte in that case.
There was a problem hiding this comment.
I still don't see how the string is terminated with a nul-byte. In my example above resp_buf contains the 5 characters a, b, c, d and e. The return value is 5. Maybe you say that the caller is responsible to not treat the result as a nul-terminated string?
There was a problem hiding this comment.
The return variable res, which defaults to -1, is only set to the number of bytes read if a full line could be read. In your example ([ a, b, c, d, e ]), the while loop would end with res == -1 and thus set *resp_buf = '\0'.
There was a problem hiding this comment.
@keestux Does this address your concerns?
There was a problem hiding this comment.
I can see that there can be no unterminated string, even when a longer line comes in. Good.
On the other hand, you take drastic measures to throw away the whole input (instead of truncating).
If this is what we want then OK.
BTW. The doc says "<0 on error", which is either a timeout (-ETIMEDOUT) or a too long input line (-1). Perhaps you want to put this in the function description (at.h).
drivers/include/at.h
Outdated
| * Most functions compare the bytes echoed by the device with what they | ||
| * intended to send, and bail out if there's no match. | ||
| * | ||
| * Furthermore, the library tries to copy with difficulties regarding different |
9bcc58c to
842d050
Compare
It probably can even be used as is for devices not speaking Hayes. The actual AT commands are all built on top of |
|
Can we get forward with this? ping @haukepetersen @aabadie @keestux |
aabadie
left a comment
There was a problem hiding this comment.
@kaspar030, @vincent-d this PR looks ok codewise. I'm +1 for merging it.
This way, we can move forward on this driver and related features.
| print(resp_pos, read_res); | ||
| } | ||
| if (*resp_pos == '\r') { | ||
| continue; |
There was a problem hiding this comment.
I have a LoRa modem that is controlled using AT commands. The replies ends with a \r, so continuing here doesn't work with it.
What about adding a macro like AT_END_OF_RESP ?
drivers/at/at.c
Outdated
| uart_write(dev->uart, (const uint8_t *)command, cmdlen); | ||
| uart_write(dev->uart, (const uint8_t *)AT_EOL, AT_EOL_LEN); | ||
|
|
||
| if (at_expect_bytes(dev, command, timeout)) { |
There was a problem hiding this comment.
My LoRa modem doesn't echo the command sent. I have a fix with another preprocessor macro: AT_CMD_ECHO which is equal to 1 by default. Would this be ok ?
| uart_write(dev->uart, (const uint8_t *)command, cmdlen); | ||
| uart_write(dev->uart, (const uint8_t *)AT_SEND_EOL, AT_SEND_EOL_LEN); | ||
|
|
||
| if (AT_SEND_ECHO) { |
There was a problem hiding this comment.
@vincent-d @kaspar030, here is the change that allows to skip the echo check at build time.
I would suggest to squash and merge as-is and improve in a follow-up PR. @kaspar030 could you squash? |
|
mmh, first, we should fix Murdock:
@aabadie ? |
done! (sorry) |
cpu/atmega_common/periph/i2c.c
Outdated
| #include "debug.h" | ||
| #define ENABLE_DEBUG (0) | ||
|
|
||
| /* guard file in case no I2C device is defined */ |
There was a problem hiding this comment.
This looks unrelated, weird.
There was a problem hiding this comment.
yes, no idea how that commit sneaked in. I squashed it out.
|
@vincent-d, ok to merge ? I think we can safely dismiss @haukepetersen's review |
|
Cool ! |
|
Thanks for helping with this! This also was the first time that two other people were pushing into my branch. Feels .... funny. 😏 |
|
Great team work! Thanks all! |
This PR adds a module for interacting with devices using AT commands.