> For the complete documentation index, see [llms.txt](https://amobile-solutions.gitbook.io/yocto-developer-guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://amobile-solutions.gitbook.io/yocto-developer-guide/test-and-use-the-interface/gnss/gnss-iot-800n-g420.md).

# GNSS - IOT-800N-G420

## Overview

|                |                                 |
| -------------- | ------------------------------- |
| Receiver       | Airoha AG3335M                  |
| LNA            | SKY65728                        |
| Interface      | SPI, `/dev/spidev0.0` at 10 MHz |
| Power control  | `/dev/airoha_gps`               |
| Antenna        | SMA and I-PEX connectors        |
| Constellations | GPS, GLONASS, Galileo, BeiDou   |

## How it fits together

```
airoha-gnssd  →  /dev/gnss0 (pty)  →  gpsd  →  cgps, gpspipe, libgps
```

`airoha-gnssd` runs at boot and does everything in one pass:

1. Enables the chip
2. Reads the firmware version over SPI
3. Compares it against the expected version, flashing if configured to
4. Sends the commands that start navigation
5. Creates a pty at `/dev/gnss0` and streams NMEA into it

`gpsd` attaches to that pty, so all the standard tools work.

{% hint style="info" %}
The AG3335M does not emit NMEA on its own after power-on. It reports its version once and then goes quiet until it receives a start command, which the daemon sends.
{% endhint %}

## Checking status

```
gps-info.sh
```

```
-- services --
  airoha-gnssd : running (unit active)
  gpsd         : active
  firmware     : AG3335M_V3.3.0.AG3335_20260515
-- fix --
  status    : 3D
  latitude  : 25.041000
  longitude : 121.520000
  satellites: 11 seen, 8 used
```

Other modes:

```
gps-info.sh live      cgps interactive view
gps-info.sh raw       raw NMEA
gps-info.sh json      JSON stream
gps-info.sh fw        firmware version
gps-info.sh diag      daemon, gpsd, pty permissions and recent log
```

## Reading NMEA directly

```
gpspipe -r -n 20
```

```
$GNGGA,235942.012,,,,,0,0,...
$GNRMC,...
$GNGSA,...
$GPGSV,...
```

Sentences appearing at all proves the whole path works, independent of whether there is a fix.

To count them:

```
n=$(timeout 15 gpspipe -r -n 30 2>/dev/null | grep -c '^\$G')
echo "$n sentences in 15s"
```

## Getting a fix

{% hint style="warning" %}
Indoors the receiver will report `NO FIX` with zero satellites. That is expected and is not a fault. The antenna needs a view of the sky.
{% endhint %}

Cold start times:

| Environment        | Time to first fix         |
| ------------------ | ------------------------- |
| Outdoors, open sky | 30 seconds to 2 minutes   |
| Near a window      | 2 to 10 minutes, or never |
| Indoors            | Generally no fix          |

There is no A-GPS or EPO, so every start is a cold start.

## Verifying without a fix

Reading the firmware version back is the strongest single check:

```
gps-info.sh fw
```

```
AG3335M_V3.3.0.AG3335_20260515
```

It proves SPI communication, the bootloader and the main firmware are all working. The daemon caches the value in `/run/airoha-gnss.version` at startup, so this does not disturb the running daemon.

## Configuration

`/etc/airoha/gnssd.conf`:

```
SPIDEV=/dev/spidev0.0
SPI_SPEED=10000000
GPSDEV=/dev/airoha_gps
PTY_LINK=/dev/gnss0
FW_DIR=/etc/airoha

EXPECT_FW=AG3335M_V3.3.0
AUTO_UPDATE=1
FLASH_IF_NO_VERSION=0
PROBE_RETRY=3
```

`EXPECT_FW` is compared as a prefix, so the build date does not have to be included. Leaving it empty disables both the check and any flashing.

## Firmware update

The daemon can reflash the receiver from the images in `/etc/airoha/`:

```
systemctl stop airoha-gnssd
airoha-gnssd --check      read the version and exit
airoha-gnssd --update     force a flash and exit
systemctl start airoha-gnssd
```

Flashing takes about 85 seconds: roughly 20 seconds to erase the 4 MB flash and 60 to write 1.5 MB of images, followed by CRC verification.

{% hint style="danger" %}
Flashing erases the entire device before writing. `FLASH_IF_NO_VERSION` defaults to `0` for that reason: a failed version read can be a transient SPI problem rather than a blank flash, and mistaking one for the other destroys a working chip.

Set it to `1` only on a production line where the parts are known to be blank.
{% endhint %}

{% hint style="warning" %}
`--check` and `--update` both claim the SPI bus, so the daemon has to be stopped first. Two processes on the bus interleave their commands and corrupt the slave state machine.
{% endhint %}

## Sending commands to the receiver

The pty is bidirectional, so PAIR commands can be issued directly:

```
printf '$PAIR050,1000*12\r\n' > /dev/gnss0
```
