Skip to content

drivers: initial commit of generic AT parser module#7084

Merged
vincent-d merged 9 commits intoRIOT-OS:masterfrom
kaspar030:add_at_parser
May 23, 2018
Merged

drivers: initial commit of generic AT parser module#7084
vincent-d merged 9 commits intoRIOT-OS:masterfrom
kaspar030:add_at_parser

Conversation

@kaspar030
Copy link
Copy Markdown
Contributor

This PR adds a module for interacting with devices using AT commands.

@kaspar030 kaspar030 requested a review from haukepetersen May 20, 2017 14:24
@kaspar030 kaspar030 added the Type: new feature The issue requests / The PR implemements a new feature for RIOT label May 20, 2017
@miri64
Copy link
Copy Markdown
Member

miri64 commented May 20, 2017

@jia200x maybe useful for your #5427

@vincent-d
Copy link
Copy Markdown
Member

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 \r to each of my commands ;)

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!

@jia200x
Copy link
Copy Markdown
Member

jia200x commented May 22, 2017

@miri64 Indeed. Thanks for pointing me to this.

Cheers

@kaspar030
Copy link
Copy Markdown
Contributor Author

It worked good once I understood I had to add a \r to each of my commands ;)

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 \n, but echo \r\n. They don't like to receive crlf, though. Maybe we need to parameterize the newline behaviour somehow?

@kaspar030
Copy link
Copy Markdown
Contributor Author

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.

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!

@vincent-d
Copy link
Copy Markdown
Member

Maybe we need to parameterize the newline behaviour somehow?

It would be handy, indeed.

I use a ublox modem, which needs a \r and echoes \r\r\n.

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented Jun 20, 2017

Any update here ? @kaspar030

Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

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

Maybe the device descriptor pointers could be made const when possible ?

@aabadie aabadie added this to the Release 2017.07 milestone Jun 23, 2017
@vincent-d
Copy link
Copy Markdown
Member

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)) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

shouldn't this be AT_END_OF_LINE "\n" as second parameter?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

And this is wanted? I don't quite see it, but if it is intended then never mind...

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

alright, I see.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

That would not hurt, indeed

Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

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

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

* @param[in] len len of @p buffer
* @param[in] timeout timeout (in usec)
*
* @returns lenght of response on success
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

before I forget, there's a typo: s/lenght/length/. I saw it in other places.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed two times int at.h.

@keestux
Copy link
Copy Markdown
Contributor

keestux commented Aug 25, 2017

@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). :-(

*
* @param[in] dev device to operate on
* @param[in] bytes buffer containing bytes to expect
* @param[in] len number of bytes to expect
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why do we need 'len'? The length of 'bytes' should be leading, otherwise you need a sanity check in the function.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't quite get the question. len specifies the nr of bytes in bytes. Maybe the docs are misleading?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That actually seems like a good idea. done.


memset(resp_buf, 0, len);

while (len) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's what the memset in line 210 should take care of?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Ah, right. I guess -ENOSPC would be the desired result in that case?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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'.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@keestux Does this address your concerns?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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).

* 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
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

/copy/cope/ ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

fixed

@kaspar030
Copy link
Copy Markdown
Contributor Author

Do you think it could be even more generalized to any string command controlled devices ? (not only AT).

It probably can even be used as is for devices not speaking Hayes. The actual AT commands are all built on top of at_expect_bytes(), at_readline() and at_send_bytes().

@kaspar030
Copy link
Copy Markdown
Contributor Author

Can we get forward with this? ping @haukepetersen @aabadie @keestux

Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

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

@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.

Copy link
Copy Markdown
Contributor

@aabadie aabadie left a comment

Choose a reason for hiding this comment

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

I did more testing with an Arduino mkrwan board. It embeds a LoRa module that is controlled by AT but not very standard apparently.
I could make it work with some adaption in this PR. See my comments below.

print(resp_pos, read_res);
}
if (*resp_pos == '\r') {
continue;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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)) {
Copy link
Copy Markdown
Contributor

@aabadie aabadie May 22, 2018

Choose a reason for hiding this comment

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

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) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@vincent-d @kaspar030, here is the change that allows to skip the echo check at build time.

@vincent-d
Copy link
Copy Markdown
Member

What about adding a macro like AT_END_OF_RESP

I would suggest to squash and merge as-is and improve in a follow-up PR.
I also have a couple of improvements I'd like to merge later on.

@kaspar030 could you squash?

@vincent-d
Copy link
Copy Markdown
Member

mmh, first, we should fix Murdock:

drivers/include/at.h:52: warning: Member AT_SEND_ECHO (macro definition) of group drivers_at is not documented.

@aabadie ?

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented May 23, 2018

we should fix Murdock

done! (sorry)

#include "debug.h"
#define ENABLE_DEBUG (0)

/* guard file in case no I2C device is defined */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks unrelated, weird.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yes, no idea how that commit sneaked in. I squashed it out.

@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented May 23, 2018

@vincent-d, ok to merge ? I think we can safely dismiss @haukepetersen's review

@haukepetersen haukepetersen dismissed their stale review May 23, 2018 15:30

seems to be all good

@vincent-d vincent-d merged commit 84874e6 into RIOT-OS:master May 23, 2018
@aabadie
Copy link
Copy Markdown
Contributor

aabadie commented May 23, 2018

Cool !

@kaspar030 kaspar030 deleted the add_at_parser branch May 23, 2018 15:51
@kaspar030
Copy link
Copy Markdown
Contributor Author

Thanks for helping with this!

This also was the first time that two other people were pushing into my branch. Feels .... funny. 😏

@vincent-d
Copy link
Copy Markdown
Member

Great team work! Thanks all!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Type: new feature The issue requests / The PR implemements a new feature for RIOT

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants