← All showcases
cross-stack file refsEmbedded ↔ backend
integrationcross-stackembedded

Firmware meets the cloud

10 Hz on the device, 50 ms on the ingest path - one invariant neither codebase can hold alone.

The contract lives in .rq because C and TypeScript cannot see each other.

contracts/ingest.rq

sensor_sample_rate {
  device samples at 10 Hz
  implemented in ["./firmware/adc.c".sample_loop]
  @tags (
      firmware
      timing
  )
}

api_ingest {
  backend must accept a burst within 50 ms
  implemented in ["./api/ingest.ts".ingestBatch]
  aligns with [sensor_sample_rate]
  @tags (
      backend
      sla
  )
}

ingest_contract (
  sensor_sample_rate,
  api_ingest
)

firmware/adc.c

/* rq:["../contracts/ingest.rq".sensor_sample_rate] */
void sample_loop(void) {
  const uint32_t period_ms = 100; /* 10 Hz */
  for (;;) {
      adc_read(&sample);
      queue_push(&sample);
      sleep_ms(period_ms);
  }
}

api/ingest.ts

// rq:["../contracts/ingest.rq".api_ingest]
export async function ingestBatch(events: Event[]) {
  const deadline = Date.now() + 50;
  await queue.push(events, { deadline });
}

in the editor

  • Inbound @referenced-by inlay on sample_loop and ingestBatch
  • Go-to-definition from either side into the shared .rq idea
  • Local graph centered on ingest_contract shows both stacks