Browse Source

build info in webusb

Jariullah Safi 4 months ago
parent
commit
65116fd808
6 changed files with 66 additions and 2 deletions
  1. 3 0
      .gitignore
  2. 22 0
      OpenPuck/build_info.h
  3. 10 2
      OpenPuck/webusb_config.cpp
  4. 3 0
      docs/BUILD_AND_DEPLOY.md
  5. 8 0
      docs/index.html
  6. 20 0
      gen_version.sh

+ 3 - 0
.gitignore

@@ -6,3 +6,6 @@ tmp/
 # pairtui helper binary (build from scmd.c: clang -framework IOKit -framework CoreFoundation -o scmd scmd.c)
 pairtui/scmd
 __pycache__/
+
+# generated build-provenance header (gen_version.sh); never commit
+OpenPuck/git_version.h

+ 22 - 0
OpenPuck/build_info.h

@@ -0,0 +1,22 @@
+// build_info.h -- firmware build provenance, surfaced in the WebUSB panel so you can confirm EXACTLY which
+// build is flashed on a board (the git commit it was built from, and whether that working tree was dirty).
+//
+// The values come from one of two sources, in priority order:
+//   1) -D defines on the compile line, e.g. build.extra_flags += -DOPK_GIT_HASH=\"abc12345\" -DOPK_GIT_DIRTY=1
+//   2) a generated git_version.h (written by gen_version.sh before compiling), picked up via __has_include.
+// If neither is present (a bare checkout compiled by hand) the values fall back to "unknown" / not-dirty, so
+// the build always succeeds -- the panel just shows "unknown" until you generate the header.
+#pragma once
+
+#if !defined(OPK_GIT_HASH) && defined(__has_include)
+#  if __has_include("git_version.h")
+#    include "git_version.h"
+#  endif
+#endif
+
+#ifndef OPK_GIT_HASH
+#define OPK_GIT_HASH "unknown"
+#endif
+#ifndef OPK_GIT_DIRTY
+#define OPK_GIT_DIRTY 0
+#endif

+ 10 - 2
OpenPuck/webusb_config.cpp

@@ -4,6 +4,7 @@
 #include "rf_link.h"
 #include "haptics.h"
 #include "puck_hid.h"
+#include "build_info.h"
 #include <Arduino.h>
 #include <string.h>
 
@@ -14,13 +15,14 @@ Adafruit_USBD_WebUSB usb_web;
 //                [qos][persistMode][chordBtn B][chordBtn X][chordBtn Y][pollsps_lo][pollsps_hi]
 //                [loopPeriod_lo][loopPeriod_hi][loopWorstIdx][loopWorstUs_lo][loopWorstUs_hi]
 //                [pollPeriod_lo][pollPeriod_hi][logEnabled][battery%][rssi|dBm|]
-#define WB_PAYLEN 38
+//                [gitDirty][gitHash 12B ASCII, NUL-padded]
+#define WB_PAYLEN 51
 static void webusbSendBlob(){
   if(!usb_web.connected()) return;
   bool up = (g_connSlot>=0 && (millis()-g_connReplyMs) < 300);
   uint8_t p[2+WB_PAYLEN];
   p[0]=0xA5; p[1]=WB_PAYLEN;
-  p[2]=3;                          // protocol version (3 = +battery/rssi at [38][39])
+  p[2]=4;                          // protocol version (4 = +git build provenance at [40..52]; 3 = battery/rssi)
   p[3]=g_usbMode; p[4]=(uint8_t)g_mDiv; p[5]=(uint8_t)g_mFric; p[6]=0 /*rsvd: ex-padSmooth*/; p[7]=g_abSwap;
   p[8]=g_back[0]; p[9]=g_back[1]; p[10]=g_back[2]; p[11]=g_back[3];
   p[12]=(g_connSlot>=0)?(uint8_t)g_connSlot:0xFF;
@@ -38,6 +40,12 @@ static void webusbSendBlob(){
   p[37]=OPK_LOG;                                                       // logging build? panel shows/hides its log UI
   p[38]=g_battery;                                                     // controller battery % (report 0x43); 0=unknown
   p[39]=g_linkRssi;                                                    // RAW signal strength |dBm| (0=no sample)
+  // Build provenance: which git commit this firmware was built from, and whether the tree was dirty. Lets the
+  // panel confirm exactly what's flashed. Injected at build time (build_info.h / gen_version.sh); "unknown" if
+  // the version header wasn't generated.
+  p[40]=OPK_GIT_DIRTY ? 1 : 0;
+  memset(&p[41],0,12);                                                 // 12B ASCII git hash, NUL-padded
+  { const char* h=OPK_GIT_HASH; for(uint8_t i=0;i<12 && h[i];i++) p[41+i]=(uint8_t)h[i]; }
   usb_web.write(p,sizeof p); usb_web.flush();
 }
 #if OPK_LOG

+ 3 - 0
docs/BUILD_AND_DEPLOY.md

@@ -47,11 +47,14 @@ arduino-cli core install adafruit:nrf52 --additional-urls https://adafruit.githu
 From the repository root:
 
 ```bash
+./gen_version.sh   # optional: stamp the build with the current git hash + dirty flag (shown in the panel)
 arduino-cli compile -b adafruit:nrf52:feather52840 --build-property "build.extra_flags=-DNRF52840_XXAA {build.flags.usb} -DCFG_TUD_HID=4" OpenPuck
 ```
 
 This sketch requires `CFG_TUD_HID=4` because Steam mode exposes four HID interfaces.
 
+**Build provenance.** `gen_version.sh` writes `OpenPuck/git_version.h` with the current commit's short hash and a dirty flag (1 if the working tree has any tracked change or untracked file). The firmware bakes these in and reports them over WebUSB, so the panel's **Build (git)** field shows exactly which commit is flashed and whether it was a clean checkout — handy for confirming what's on a board. The header is git-ignored; if you skip the script the build still succeeds and the panel shows `unknown`. You can also inject the values directly instead of using the script, e.g. append ``-DOPK_GIT_HASH=\"$(git rev-parse --short=8 HEAD)\" -DOPK_GIT_DIRTY=0`` to `build.extra_flags`.
+
 ## 5. Upload the firmware
 
 ### macOS / Linux

+ 8 - 0
docs/index.html

@@ -88,6 +88,7 @@
         <div class="stat logonly"><div class="k">Loop period</div><div class="v" id="stLoop">—</div></div>
         <div class="stat logonly"><div class="k">Slowest stage</div><div class="v" id="stWorst">—</div></div>
         <div class="stat logonly"><div class="k">Poll period act/want</div><div class="v" id="stPollPer">—</div></div>
+        <div class="stat"><div class="k">Build (git)</div><div class="v" id="stBuild">—</div></div>
       </div>
       <p class="note logonly">Polls/s ≈ 250 expected; it's capped by Loop period (≈4000 µs needed). "Slowest stage" is the loop section eating the most time per iteration.</p>
       <div class="row" style="margin:10px 0 0"><button id="hapClear">Clear stuck buzz</button>
@@ -241,6 +242,13 @@ function applyBlob(p){
   $("#stLoop").textContent = loopUs? loopUs+" µs" : "—";
   $("#stWorst").textContent = loopUs? (WORST[worstIdx]||worstIdx)+" "+worstUs+"µs" : "—";
   $("#stPollPer").textContent = up? (pollUs+" / "+pollIntended+" µs") : "—";
+  // build provenance (protocol v4+): dirty flag at p[38], 12-byte NUL-padded git hash at p[39..50]. Independent
+  // of link state. Older firmware sends a shorter blob -> stays "—".
+  const gitDirty=(p.length>38?p[38]:0); let gitHash="";
+  for(let i=39;i<51 && i<p.length && p[i];i++) gitHash+=String.fromCharCode(p[i]);
+  $("#stBuild").innerHTML = gitHash
+    ? (gitHash + (gitDirty ? ' <span class="pill dn">dirty</span>' : ' <span class="pill up">clean</span>'))
+    : "—";
   // sliders (only if not being dragged)
   setSlider("mDiv",mDiv); setSlider("mFric",mFric);
   $("#abSwap").textContent = abSwap? "on":"off"; $("#abSwap").classList.toggle("active",!!abSwap);

+ 20 - 0
gen_version.sh

@@ -0,0 +1,20 @@
+#!/bin/sh
+# gen_version.sh -- write OpenPuck/git_version.h with the current git short hash + dirty flag, for the build
+# provenance shown in the WebUSB panel. Run this BEFORE `arduino-cli compile` (it's a no-op to re-run). The
+# generated header is git-ignored, so generating it never itself marks the tree dirty.
+set -e
+cd "$(dirname "$0")"
+
+HASH=$(git rev-parse --short=8 HEAD 2>/dev/null || echo unknown)
+# Dirty = any tracked change OR untracked file (porcelain is empty only on a clean tree). git_version.h is
+# git-ignored so it doesn't count.
+if [ -z "$(git status --porcelain 2>/dev/null)" ]; then DIRTY=0; else DIRTY=1; fi
+
+cat > OpenPuck/git_version.h <<EOF
+// AUTO-GENERATED by gen_version.sh -- do not edit, do not commit (git-ignored).
+#pragma once
+#define OPK_GIT_HASH "$HASH"
+#define OPK_GIT_DIRTY $DIRTY
+EOF
+
+echo "OpenPuck/git_version.h: hash=$HASH dirty=$DIRTY"