Skip to main content

Sensors and Events

While a Kamoji Mini App is on screen, Kamoji writes its live sensor values into global tables 10 times a second. Read them in bindings ("{{ imu.lat_g }}") or in Lua.

A value is nil until its first reading arrives, which takes up to a tenth of a second after the mini app opens, so guard it: imu.lat_g or 0.

imu: motion​

Declare sensors.imu. Axes as everywhere in the Kamoji firmware:

  • x: sideways, positive to the right
  • y: along the car, positive when accelerating
  • z: vertical

The accelerations are measured from Kamoji's resting position, which it learns when it calibrates at power-up, so all three read about 0 while the car is still or cruising steadily.

ValueUnitMeaning
imu.accel_x, imu.accel_y, imu.accel_zm/s²Acceleration on each axis
imu.gforce_x, imu.gforce_y, imu.gforce_zgThe same in g
imu.lat_ggSideways g: the same as gforce_x
imu.long_ggForward g: the same as gforce_y (positive accelerating, negative braking)
imu.gyro_x, imu.gyro_y, imu.gyro_z°/sRotation speed around each axis

drive: the Excite-O-Meter's scores​

Declare sensors.imu. The values behind the Excite-O-Meter, computed by Kamoji from the motion sensor:

ValueRangeMeaning
drive.excitement0–100Excitement score: how lively the driving has been, averaged over the last 30 seconds
drive.comfort0–100Comfort score: how smooth it has been, averaged over the last 30 seconds
drive.excitement_now0–100Excitement right now
drive.comfort_now0–100Comfort right now
drive.hard_turntrue / falsetrue while the car is cornering hard
drive.hard_braketrue / falsetrue while it is braking hard
drive.hard_acceltrue / falsetrue while it is accelerating hard
drive.hardtrue / falsetrue while any of the three is

The hard_* values are states, true for as long as the move lasts; the events below fire once per move.

light​

Declare sensors.light.

ValueUnitMeaning
light.rawVThe ambient light sensor's reading, smoothed. It is not calibrated to lux: compare it with earlier readings rather than with a fixed number

env​

Declare sensors.env.

ValueUnitMeaning
env.temp_c°CThe motion sensor chip's temperature: Kamoji's own warmth, not the cabin's

time​

No permission needed.

ValueMeaning
time.validtrue once Kamoji knows the time, else false
time.epochSeconds since 1 January 1970, UTC
time.hour, time.min, time.secTime of day in the firmware's own time zone (see below)
time.day, time.month, time.yearDate, same time zone
time.weekday1 = Sunday … 7 = Saturday

Kamoji learns the time from the internet when it is on Wi-Fi, or from your phone when the Kamoji app connects; either one makes time.valid true. Until then, time.valid is false and the other values are nil.

time.hour and its neighbours use the time zone built into the firmware, which is not necessarily the user's. For a clock, give your mini app a utc_offset setting and use clock() instead:

local t = clock(param.utc_offset) -- nil until the time is known
if t then print(fmt("%02d:%02d", t.hour, t.min)) end

clock(offset) returns { year, month, day, hour, min, sec, weekday } for time.epoch shifted by offset hours (fractions like 5.5 or 5.75 work). It does not apply daylight saving time; users change the offset in the mini app's settings.

device​

ValueUnitMeaning
device.uptime_ssTime since Kamoji started

Declare phone.navigation. Turn-by-turn navigation from the phone: on Android the Kamoji app forwards Google Maps' navigation (the user turns it on, and grants notification access, after installing a mini app with this permission). Kamoji also accepts it from the Chronos app.

KeyMeaning
nav.activetrue while navigation is on
nav.routingtrue while a route is being followed
nav.titleDistance to the next turn, e.g. "250 m"
nav.directionsThe instruction, e.g. "Turn left onto Orchard Road"
nav.distanceDistance left to the destination
nav.durationTime left
nav.etaArrival time
nav.has_icontrue when there is a turn arrow
nav.linkedtrue while a phone is connected to Kamoji over Bluetooth. Show "Waiting for your phone" when it is false (a restarted Kamoji is reconnected by the app within seconds)
nav.trafficTraffic on the rest of the route: "clear", "slow", "heavy", or "" when unknown (Android 16 and later, Google Maps)
nav.stripThe road ahead (next 20 km) in 24 cells: c clear, s slow, h heavy, - beyond the end of the route
nav.has_text_imagetrue when the phone drew the instruction itself (scripts Kamoji's fonts lack): show builtin:nav_text instead of nav.directions

The arrow is an image: "src": "builtin:nav_icon" (48 x 48) or "builtin:nav_icon_large" (96 x 96), white on transparent. All texts are as the phone sends them, already in the user's language and units.

Things to know when you lay out a navigation mini app:

  • Google Maps often puts the distance and the instruction in one title, such as "8.6 km · At the roundabout, take the 2nd exit", and leaves nav.directions empty. Split the title on · to show the distance big and the instruction under it (the Navigation example does this).
  • Kamoji's fonts cover Latin (including Vietnamese) and Thai in the small and medium sizes; the large and huge sizes are Latin only, so use them for distances and times. For other scripts (Khmer, Burmese, Chinese, Arabic and so on) the phone sends the instruction as a 224 x 56 image: "src": "builtin:nav_text", shown when nav.has_text_image is true.
  • Colour the arrival time by nav.traffic the way Google Maps does: green for clear, orange for slow, red for heavy.

obd: car data (OBD-II)​

Declare sensors.obd. Live readings from the car's OBD-II port, through an ELM327 Bluetooth adapter the user paired in Kamoji's settings (firmware 0.2.2 and later). While a mini app with this permission is on screen, Kamoji keeps asking the car for them: RPM about 10 times a second, speed once a second, fuel level and voltages every 10 to 30 seconds.

KeyUnitMeaning
obd.availabletrue when this firmware offers OBD-II adapter pairing
obd.connectedtrue while the paired adapter is connected and ready
obd.rpmrpmEngine speed
obd.speedkm/hVehicle speed
obd.fuel%Fuel level
obd.voltageVBattery (control module) voltage
obd.battery%EV high-voltage battery level (cars that report it)
obd.has_rpm, obd.has_speed, obd.has_fuel, obd.has_voltage, obd.has_batterytrue once the car answered that reading on this connection
obd.tries_rpm, obd.tries_speed, obd.tries_fuel, obd.tries_voltage, obd.tries_batteryHow often it was asked on this connection

A reading the car never answered stays 0, so check its has_ flag before showing it. Electric cars answer no RPM and no fuel level: when obd.tries_rpm reaches a few (say 4) while obd.has_rpm and obd.has_fuel are still false, treat the car as an EV and show speed and obd.battery instead. If nothing at all answers after a few tries, the car doesn't support these readings (or the ignition is off).

function phase()
if not obd.available then return "unavailable" end
if not obd.connected then return "disconnected" end
local asked = (obd.tries_rpm or 0) >= 4
if asked and not obd.has_rpm and not obd.has_fuel then
return obd.has_speed and "ev" or "unsupported"
end
return obd.has_rpm and "ok" or "reading"
end

Events​

Declare events.drive. These are the same events that drive Kamoji's own reactions and its Black Box. Each one calls a handler in main.lua with its magnitude, or runs the matching entry of the layout's on (without the magnitude):

EventHandlerMagnitude
bumpon_bump(m)Size of the vertical jolt, m/s² (positive). One call per jolt
hard_brakeon_hard_brake(m)Braking deceleration, m/s² (positive)
hard_accelon_hard_accel(m)Acceleration, m/s² (positive)
leanon_lean(m)Sideways acceleration of hard cornering, m/s²: positive to the right, negative to the left

Divide by 9.81 for g. hard_brake, hard_accel and lean fire once when the value crosses Kamoji's threshold, and again only after it has settled back. Events start once Kamoji has finished starting up; hard_brake, hard_accel and lean also wait until it has calibrated. Like everything else, events only reach the mini app on screen.

state.bumps = 0
function on_bump(m)
state.bumps = state.bumps + 1
state.last = fmt("%.1f g", m / 9.81)
end

Gestures​

Declare touch. on_top_tap(), on_top_hold(), on_pinch(), and on_left_tap() / on_right_tap() for mini apps that take the side taps: see Handlers.

Coming later​

The microphone (mic), tilt angles, light in lux, and the events bumpy_road, cornering, idle_start / idle_end, Wi-Fi and phone connection events are designed but not available yet. The gyro and mic tables exist but stay empty.