Skip to content

Sensors and readings

Robot.configure() creates sensor objects. Each sensor's read() method returns the latest cached sample. It does not block.

Note

You may configure at most 2 IMU sensors and at most 1 RadioBeacon sensor. configure() raises a ValueError if you configure more.

Sensor

Sensor(port: str, state_mirror=None)

Base class for sensor types. It is public for extension and advanced use. Normally, pass concrete classes to configure() instead of constructing sensors directly.

Attributes

sensor.port: str
sensor.type: str

read

sensor.read() -> Reading

Input: none. Output: a reading object holding the latest cached sample.

Returns a base reading. Concrete sensor subclasses return their typed reading: IMU.read() returns an ImuReading, RadioBeacon.read() returns a RadioBeaconReading.

Call read() once per loop and keep the result in a variable. Each call returns a fresh object, so comparing two separate calls compares two snapshots:

reading = robot.imu("S1").read()   # one snapshot, used twice below
if reading.ok:
    print(reading.gyro_z)

Reading

Reading(timestamp: int, age: float, ok: bool)

Base dataclass shared by every reading. A dataclass is a plain Python class that holds named fields. You read its fields. You never build one yourself.

Field Type Meaning
timestamp int Robot-brain sample time in milliseconds
age float Sample age in milliseconds
ok bool True only when the payload is valid and the age is at most 250 ms

IMU

robot.imu(port: str | None = None) -> IMU
imu.read() -> ImuReading

Nine-axis inertial measurement unit. It reports acceleration, angular rate, and raw magnetometer values. It does not report heading, bearing, or orientation.

Always name the port you declared in configure():

reading = robot.imu("S1").read()

ImuReading

ImuReading(
    timestamp: int,
    age: float,
    ok: bool,
    accel_x: float,
    accel_y: float,
    accel_z: float,
    gyro_x: float,
    gyro_y: float,
    gyro_z: float,
    mag_x: int,
    mag_y: int,
    mag_z: int,
)
Fields Unit
accel_x, accel_y, accel_z g
gyro_x, gyro_y, gyro_z degrees per second
mag_x, mag_y, mag_z raw integer values
reading = robot.imu("S1").read()
if reading.ok:
    print(reading.gyro_z)

RadioBeacon

robot.radiobeacon(port: str | None = None) -> RadioBeacon
beacon.read() -> RadioBeaconReading

LoRa radio-beacon telemetry sensor. It reports signal strength and beacon identity. It does not report distance.

Always name the port you declared in configure():

beacon = robot.radiobeacon("S6").read()

RadioBeaconReading

RadioBeaconReading(
    timestamp: int,
    age: float,
    ok: bool,
    rssi: float,
    snr: float,
    beacon_id: int,
    rx_count: int,
)
Field Unit or type Meaning
rssi dBm LoRa signal strength
snr dB LoRa signal-to-noise ratio
beacon_id int Identifies which beacon sent the packet
rx_count int Counts packets received from the beacon

RSSI shows signal strength. It does not show distance. A stronger signal usually means the beacon is nearer, but RSSI is not a distance measurement.

RSSI uses a logarithmic scale. Its unit is dBm. Readings are almost always negative numbers.

A value closer to zero means a stronger signal. For example, -40 dBm is a strong signal. -90 dBm is a weak signal.

The example below uses -80 as a threshold. This threshold is not a fixed distance. It is only an example. Test the threshold with your own beacon and your own field. Choose your own threshold from that test.

beacon = robot.radiobeacon("S6").read()
if beacon.ok and beacon.rssi >= -80:
    ...

Missing and stale samples

Before a sample arrives, typed readings show these values:

  • timestamp is 0.
  • age is math.inf.
  • ok is False.
  • All sensor fields are zero.

The sensor keeps the last sample available after it becomes stale. ok becomes False when the sample age is more than 250 ms.

Always check ok before you use a reading to command motion.