-
Notifications
You must be signed in to change notification settings - Fork 651
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 support for reading NMEA sentences from I2C #3881
base: master
Are you sure you want to change the base?
Conversation
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.
Just a review of the HIL for now. I'm wondering whether we should, at some point, introduce a higher-level HIL that can actually represent position data meaningfully, but a low-level NMEA-Sentence HIL seems fine to me too. It should have its semantics documented more clearly though (i.e. overall end-to-end behavior, and edge cases like timeouts & errors).
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.
This looks reasonable to me. The design looks good enough that the bottom could be swapped out for a UART implementation (which is common) in the future without issue.
Way back in the Signpost days, we also just exposed NMEA messages to userspace, because it's way easier to parse them there. There are various C libraries that will do this for you.
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 would be supportive of a HIL that provides location, but I'm not sure about the benefit of a HIL for NMEA strings. I think that trait can just be in capsules.
4826575
to
0eba1a7
Compare
NMEA can be exposed over UART, I2C or BLE (probably other things as well) so I think it makes sense to use a HIL. That way in the future we can expand the implementations |
0eba1a7
to
383c2ed
Compare
I agree with @bradjc about a HIL, particulary one in the sensors (fwiw, NMEA is used not only for sensors, but also actuators, like autopilots). It doesn't need to be a HIL for there to be a trait that's implemented for different serial interfaces. In particular, I'd prefer to have that trait be in capsules (e.g. the thing that is a client of the NMEA can define the trait), and I'd prefer for that to come up when there is at least a second implementation |
Are all uses of NMEA over I2C or all uses over UART or ditto over SPI, etc always the same framing interface? E.g. is an I2C implementation guaranteed to always work for all NMEA exposed over I2C? Or does each GPS/NMEA peripheral actually have its own specific I2C protocol? |
383c2ed
to
838a6ff
Compare
Yes. The format of a NMEA sentence is defined. It doesn't matter what device or transport method you use |
I moved the trait from a HIL to a capsule |
@alistair23 sure, but what I meant was are NMEA sentences always accessible over I2C in the same way? For example, it's pretty common for I2C peripherals to have a particular "register" you read different "sensor" data from. In this implementation, it looks like the expectation is that the device will just spit out NMEA over I2C with no other framing, but is that always the case? E.g. this seems to leave no room for configuring the device (GPS peripherals often have a way to switch them to lower/higher power modes, communicate saved position fixes, etc). |
From what I have seen, most devices expose NMEA sentences in the same way. The only thing that changes is the address of the device. The one big difference is the u-blox DDC mechanism. It allows reading/writing configuration registers via I2C. BUT if you read from the Even if there is a device that does something a different way, it can still use this trait and just modify the operations to access the NMEA data in the device specific way. You could configure devices by performing I2C write operations, the same way you could configure the device over serial by writing to the UART. That wouldn't go through this interface though, it would be a device dependent configuration. |
a3d56f2
to
330acf7
Compare
23e12c9
to
fb0d4ab
Compare
Ping! |
I'm still nervous about adding the Right now this feels like a very specific tool for a very niche use case, which there is nothing wrong with, but with upstreaming it I don't want to push the rough edges on to the next user. |
Yeah, as is, this doesn't seem on path to "right." I'll try to articulate the totality of why I think that and maybe we can either understand if I'm mistaken, or else what the right path to "right" should be. First, I think broadly NMEA in general, and GPS in particular, is totally in scope for support. There are three ways I think this could play out well, not necessarily mutually exclusively:
I think this PR is closest to trying to provide 3.
|
The syscall driver is virtualized to support multiple apps. // Read sentence
1 => self
.apps
.enter(processid, |app, _| {
app.operation = Operation::Read;
self.buffer.take().map_or(CommandReturn::success(), |buf| {
match self.driver.read_sentence(buf) {
Ok(()) => CommandReturn::success(),
Err((e, buffer)) => {
self.buffer.replace(buffer);
app.operation = Operation::None;
CommandReturn::failure(e)
}
}
})
})
.unwrap_or_else(|err| CommandReturn::failure(err.into())), If a sentence has already been requested by a different app the driver just marks the app as a reader. When the sentence is ready it is copied to all apps. fn callback(&self, buffer: &'static mut [u8], len: usize, status: Result<(), ErrorCode>) {
for cntr in self.apps.iter() {
cntr.enter(|app, kernel_data| {
if app.operation == Operation::Read {
...
let _ = kernel_data.get_readwrite_processbuffer(0).and_then(|dest| {
dest.mut_enter(|dest| {
let copy_len = dest.len().min(len);
dest[0..copy_len].copy_from_slice(&buffer[0..copy_len]);
})
});
app.operation = Operation::None;
kernel_data.schedule_upcall(0, (into_statuscode(Ok(())), len, 0)).ok();
}
});
}
...
} |
Ok @bradjc amended my comment, I read that code wrong. |
I think, then, the current status is that both @bradjc and I are skeptical about this If that is resolved, I think there is room for, particularly, better documentation (specifically of the system call interface), but might be good otherwise. |
I find this a little hard to follow what exactly you are asking. The GNSS device is some unnamed MediaTek device. It's possibly https://cdn.sparkfun.com/assets/parts/1/2/2/8/0/GTOP_NMEA_over_I2C_Application_Note.pdf but I'm not sure exactly. It has an antenna stuck on top of the SoC so it's hard to tell.
I'm not sure I follow, especially the odd chip interface. From my understanding Brad's proposal is to create a ~300 byte buffer to store the sentences in and read them continuously. The goal being that we never loose any part of the buffer. As I pointed out in #3881 (comment), that results in a pretty large buffer. It doesn't really guarantee you don't loose anything if userspace isn't quick enough. It also falls apart with low power mode. I can rename The other option might be for someone else to grab a GNSS device, hook it up and test if this works for them. I've had this one running for hours and don't see any issues with missing sentences. Userspace is easily able to decode the data and obtain a location |
How are you checking if messages are getting dropped? From my understanding, the only way this could receive every NMEA message is if all NMEA messages from this chip are a multiple of
I'm not sure that is that large of a buffer, but, in any case, that may just be the cost of writing a robust driver. No one would use NMEA if they were aggressively trying to reduce memory as any ASCII protocol is going to have significant overhead. Have you measured the power? This seems like not the way to write a low power driver. I would expect an interrupt driven interface rather than a polling one like this. I'm also worried about clock stretching. If this driver issues a 24 byte read, but the chip only has 1 byte ready, the chip has to clock-stretch until the next 23 bytes are available. If that takes 1 second, the bus is consumed for that entire interval, and nothing else can use I2C. |
You would also get every message if you read fast enough that the next message isn't in the buffer yet. I more meant that I receive all of the NMEA types, not that I guarantee that I get every message.
It is a large buffer and all of these buffers add up. You also need a buffer in userspace to copy it to. This ends up wasting memory, which Tock systems are generally limited on. AFAIK MediaTek GNSS devices only support NMEA. So unless you are using u-blox, your custom silicon or maybe other vendors you are going to use NMEA. Even for other devices NMEA is a go to. It's easy to decode and there are a bunch of existing libraries for it.
Yes, I have measured the power. The GNSS when running uses a large amount of power (~26mA compared to ~8mA from Tock). I can set the GNSS device to lower power, which basically puts the GNSS to sleep and it wakes up at some schedule, depending on the low power mode. Then from userspace I can just attempt a read. If the GNSS device is asleep it Nacks the transaction (really it just doesn't Ack it) so userspace gets an error. Then userspace can sleep and try again. I can also wait for an interrupt from the GNSS device via GPIO and then start the read after that. That requires connecting an interrupt pin though, which is a bit trickier to setup. At least my device also supports GPIO wakeups, but I haven't tried that.
I haven't found a single GNSS device that clock stretches. Let me know if you know of any that do. Everything I see just Nacks when you read the FIFO. |
Oh! This is helpful to know! This workflow makes a lot more sense, but isn't in the code or the PR description. I didn't know how this driver is working. |
I always thought clock stretching was very uncommon. Although it's allowed it is rarely used/supported. I can add a comment to the code saying we expect the device to Nack when the FIFO is empty if that helps? |
Signed-off-by: Alistair Francis <alistair@alistair23.me>
Signed-off-by: Alistair Francis <alistair@alistair23.me>
Signed-off-by: Alistair Francis <alistair@alistair23.me>
Signed-off-by: Alistair Francis <alistair@alistair23.me>
Signed-off-by: Alistair Francis <alistair@alistair23.me>
Signed-off-by: Alistair Francis <alistair@alistair23.me>
Signed-off-by: Alistair Francis <alistair@alistair23.me>
fc7b671
to
78656f7
Compare
Comment added |
Ping! |
Ping! |
Also, the waiting-on-author tag doesn't really work. As it doesn't get removed when no longer waiting on the author |
Ping! |
Pull Request Overview
NMEA is a common output from GNSS devices. It is a "sentence" that starts with a
$
.This adds support for userspace to read NMEA sentences from a GNSS device connected via I2C.
Testing Strategy
Reading NMEA sentences from I2C.
TODO or Help Wanted
Documentation Updated
/docs
, or no updates are required.Formatting
make prepush
.