Source: hardware/BUILD.md
HerdTag v1: hardware build plan
Batch: 10 working tags plus 2 spares (buy for 12), and 1 gateway. Band: US915. Builder: comfortable with soldering and firmware. Makerspace: FDM printer, SLA printer, soldering stations.
Every price below is an estimate from listings checked on 2026-11-07. Prices move, and RAK's own store doesn't always include US tariffs. Anything marked VERIFY couldn't be confirmed online and needs checking before you order or build.
1. Architecture
Each tag is an off-the-shelf RAKwireless WisBlock stack:
- RAK4631 core: an nRF52840 MCU and an SX1262 LoRa radio.
- RAK19007 base board: battery charger plus solar input.
- RAK12500 GNSS module in slot A.
- RAK1904 accelerometer in slot C.
- A small hand-soldered cue board driving a sealed piezo and, optionally, a coin vibration motor.
The fence logic runs on the tag (firmware/, a C port of src/herdtag/controller.py). The tag sends 11-byte position reports over LoRaWAN to one farm gateway, which forwards them to The Things Stack and then by webhook to our backend. Fences come back down as fragmented downlinks.
┌──────────────── tag (in sealed case, bolted to ear-tag panel) ────────────────┐
GNSS patch ─────┤ RAK12500 (slot A, I2C) RAK1904 LIS3DH (slot C, INT1→WB_IO3) │
antenna │ \ / │
915 MHz FPC ────┤ RAK4631 core (nRF52840 + SX1262) ── WB_A1 ─▶ MOSFET ─▶ piezo (cue) │
(+ BLE FPC) │ │ ── WB_IO1 ─▶ MOSFET ─▶ coin motor (opt.) │
│ RAK19007 base: charger ◀── LiPo (JST PH 2.0) ◀── solar 5 V (JST ZH 1.5) │
└───────────────────────────────────────────────┬───────────────────────────────┘
│ LoRaWAN US915 FSB2, 11 B FPort 1 ↑ / fence frags FPort 2 ↓
RAK7268V2 gateway (Ethernet/Wi-Fi backhaul)
│
The Things Stack Sandbox (nam1) ── webhook ──▶ backend POST /ingest/ttn
2. Platform check: what I verified
| Part | Verified facts | Source |
|---|---|---|
| RAK4631 core | nRF52840 + SX1262. The US915 variant is "RAK4631(H)"; the DigiKey PN for the 915 MHz version is RAK4631-9-WB-I. 20 × 30 mm. About 2 µA sleep. Antennas ship in the box: VERIFY (RAK links the RAKARB03 902–928 MHz antenna) | RAK datasheet, DigiKey |
| RAK19007 base | 30 × 60 mm. Slots: CPU, IO, sensor A–D. Battery: JST PHR-2, 2.0 mm pitch, 3.3–4.3 V, max 4.3 V. Solar: JST ZHR-2, 1.5 mm pitch, 5 V panel, max 5.5 V. Header J11 = AIN1 / IO1 / IO2 / VBAT, J12 = 3V3 / GND / SCL / SDA, J10 = BOOT / UART1. Buy ver C (ver B also listed) | RAK datasheet, DigiKey RAK19007-0-BB-N |
| RAK12500 GNSS | u-blox ZOE-M8Q, UART + I2C, slot A (UART/I2C) or slot C (I2C only), 10 × 23 mm. Retailers list a GPS antenna (IPEX) in the box. ⚠ Rokland says u-blox has sunset the ZOE-M8Q and the RAK12500 will be discontinued once stock runs out. | RAK datasheet, Rokland, Core Electronics (box contents) |
| RAK12501 GNSS (fallback) | Quectel L76K, UART only, slot A or D. About 18 mA tracking, 7 µA power-save. Power switched via WB_IO2. Antenna in box: VERIFY | RAK datasheet |
| RAK1904 accel | ST LIS3DH, I2C, slots A/C/D(/E/F). Interrupt per slot: A→IO1, B→IO2, C→IO3, D→IO5. Avoid slot B (IO2 switches 3V3_S). 10 × 10 mm | RAK datasheet |
| RAK7268V2 gateway | WisGate Edge Lite 2, 8-channel indoor, US915 non-LTE about $170 at a US reseller | RAK datasheet, reseller listing |
| Arduino pin numbers | WB_IO1=17, WB_IO2=34, WB_IO3=21, WB_IO4=4, WB_IO5=9, WB_IO6=10, WB_A0=5, WB_A1=31, I2C SDA 13 / SCL 14, UART1 RX 15 / TX 16 |
RAK BSP variant.h |
| Battery ADC | PIN_VBAT = WB_A0, divider compensation 1.73 in RAK's example. RAK staff say nominal is 1.667. Calibrate against a multimeter, on battery, USB unplugged |
RAK example, forum |
Substitution decision:
- Buy the RAK12500 while stock exists. It's lower power, and it uses the u-blox protocol, which carries straight into a v2 on the MAX-M10S.
- If it's sold out, use the RAK12501. That means slot A and UART, and the firmware GNSS driver changes from u-blox UBX to NMEA/CASIC.
3. Firmware pin map (for firmware/tag/pins.h)
| Define | Value | Arduino # | Where it physically is | Notes |
|---|---|---|---|---|
PIN_CUE |
WB_A1 |
31 (P0.31) | RAK19007 header J11 "AIN1" | Used as a digital PWM output; the nRF52 PWM can route to any GPIO. Drives the piezo MOSFET gate |
PIN_VIBE |
WB_IO1 |
17 | RAK19007 header J11 "IO1" | Optional coin motor. VERIFY in the WisBlock Pin Mapper that the RAK12500 in slot A doesn't use IO1. If it does, leave the motor unpopulated |
PIN_GNSS_POWER |
WB_IO2 |
34 | (internal) switches 3V3_S | Cutting 3V3_S loses the GNSS hot-start state. Measure u-blox power-save/backup modes against a full rail cut on the PPK2 |
| GNSS bus | Wire (I2C) |
SDA 13 / SCL 14 | Slot A | u-blox DDC address 0x42 |
PIN_ACCEL_INT |
WB_IO3 |
21 | Slot C → INT1 | LIS3DH I2C address 0x18 or 0x19: VERIFY with an I2C scan |
PIN_VBAT |
WB_A0 |
5 | (internal) divider | Compensation 1.73, then calibrate per board |
Firmware conflict: the current placeholders in
firmware/tag/pins.h(PIN_CUE WB_IO3,PIN_VIBE WB_IO4) need changing. WB_IO3 is the accelerometer interrupt in slot C, and WB_IO4 isn't broken out on the RAK19007 headers. Use the values above.
4. Cue driver ("cue board"): one per tag, on a 20 × 30 mm perfboard
VBAT (3.7 V from J11 pin 4)
│ │
├──────────┐ ├───────────┬─────────┐
│ │ │ │ │
[PIEZO] [R3 1k] [MOTOR] [D1 ▲] [C2 100n]
│ AT-1442 │ 10 mm coin 1N4148W (across motor)
│ │ │ (cathode→VBAT) │
├──────────┘ ├───────────┴─────────┘
│ D │ D
Q1 AO3400A Q2 AO3400A Q = N-MOSFET, SOT-23 (logic-level, Vgs(th) ≤ 1.45 V)
│ S │ S
GND GND
PIN_CUE (WB_A1) ──[R1 100Ω]──┬── G(Q1) PIN_VIBE (WB_IO1) ──[R4 100Ω]──┬── G(Q2)
[R2 100k] [R5 100k]
│ │
GND GND
Decoupling: C1 10 µF + 100 nF from VBAT to GND at the board's VBAT entry.
- Piezo: use a PUI AT-1442-TT-WP-R, externally driven, IP67, resonant at 4.25 kHz, 84 dB at 10 V and 10 cm (DigiKey).
- Drive it with a ~4.25 kHz, 50% square wave on
PIN_CUE. - At 3.7 V it will be noticeably quieter than its 84 dB rating. That's a built-in loudness cap.
- R3 bleeds the piezo's charge so the MOSFET can switch it.
- Coin motor: a 3 V, 10 mm coin vibration motor. Drive it with on/off pulses (or ≤ 1 kHz PWM if you need lower strength). D1 is the flyback diode.
- Wiring: 30 AWG silicone wire from header J11. Pin order on J11 is AIN1 / IO1 / IO2 / VBAT; GND comes from J12. VERIFY pin 1 orientation on the silkscreen.
5. Bill of materials (buy for 12 tags)
5a. Per-tag electronics
| Part | Part number | Qty | Est. unit | Est. line | Buy from | Notes |
|---|---|---|---|---|---|---|
| WisBlock core, US915 | RAK4631-9-WB-I (RAK4631(H) US915) | 12 | $21 | $252 | DigiKey / Rokland / RAK store | Make sure it's the 915 variant |
| WisBlock base | RAK19007 (ver C) | 12 | $11–15 | $156 | DigiKey / Rokland / RAK store | RAK store lists $9.99 + shipping + tariff |
| GNSS module | RAK12500 (fallback RAK12501) | 12 | $27 | $324 | Rokland (US stock) | Includes IPEX GPS patch antenna per retailers. Sunset part: order early |
| Accelerometer | RAK1904 | 12 | ~$8 VERIFY | $96 | Rokland / RAK store | Slot C |
| LoRa antenna, 915 MHz FPC, U.FL/IPEX | RAK RAKARB03 or Molex 211140-0100 (VERIFY 915 band and U.FL) | 12 | $3–4 | $45 | RAK / DigiKey | Skip if the RAK4631 box includes one |
| BLE antenna, 2.4 GHz FPC, U.FL | Any 2.4 GHz U.FL FPC (e.g. Molex 146153-0100, VERIFY) | 12 | $3 | $36 | DigiKey | Needed for BLE DFU and debug. Skip if included |
| Subtotal | ~$909 |
5b. Cue actuators and cue board
| Part | Part number | Qty | Est. unit | Est. line | Buy from | Notes |
|---|---|---|---|---|---|---|
| Sealed piezo transducer | PUI AT-1442-TT-WP-R | 14 | $3 | $42 | DigiKey | 14 mm body: measure it into the CAD |
| N-MOSFET, logic level | AO3400A (SOT-23) | 50 | $0.15 | $8 | DigiKey / LCSC | Alt: DMG2302U |
| SOT-23 to DIP breakout | generic | 30 | $0.30 | $9 | Amazon / Adafruit | Or dead-bug onto perfboard |
| Coin vibration motor, 10 mm, 3 V | e.g. Adafruit 1201 | 12 | $2 | $24 | Adafruit / DigiKey | Optional for the first build |
| Diode 1N4148W, resistors 100 Ω / 1 k / 100 k (0805 or TH), caps 100 nF, 10 µF | assortment | 1 | $15 | $15 | DigiKey | |
| Perfboard, 30 AWG silicone wire, heat-shrink | 1 | $25 | $25 | |||
| Subtotal | ~$123 |
5c. Power
| Part | Part number | Qty | Est. unit | Est. line | Buy from | Notes |
|---|---|---|---|---|---|---|
| LiPo 3.7 V 1000 mAh with protection PCM, JST PH 2.0 | 503450 size (~50 × 34 × 5 mm) | 14 | $7 | $98 | Adafruit #258-class / Amazon | Polarity varies by vendor: check with a meter before every first plug-in. About 20 g. For weight-optimized tags later, 503035 500 mAh (~10 g) |
| Solar panel, 5 V, ~0.5–1 W, small | generic 5 V mini panel | 4 | $4 | $16 | Amazon / Adafruit | Bench and charging test only. Never over 5.5 V |
| JST ZH 1.5 mm 2-pin pigtails | 1 pack | $8 | $8 | Amazon | For the solar input | |
| Subtotal | ~$122 |
5d. Gateway
| Part | Part number | Qty | Est. unit | Est. line | Buy from | Notes |
|---|---|---|---|---|---|---|
| LoRaWAN gateway, 8-ch, US915, no LTE | RAK7268V2 (WisGate Edge Lite 2) | 1 | $170 | $170 | Rokland / RAK / reseller | Indoor: mount by a high window or in a vented weatherproof box |
| Outdoor antenna option | RAK 915 MHz fiberglass ~5.8 dBi + low-loss cable (N to the gateway's connector; VERIFY the RP-SMA vs. SMA gender on the V2) | 1 | $60–90 | $75 | RAK / Rokland | Height beats gain. Get it on the roof line |
| Outdoor gateway (alternative) | RAK7289V2 / RAK7267 class | — | ~$400+ | — | If indoor coverage fails the walk test | |
| Subtotal | ~$245 |
5e. Tools and consumables (skip what the makerspace already has)
| Item | Part number | Qty | Est. | Notes |
|---|---|---|---|---|
| Power profiler | Nordic PPK2 (NRF-PPK2) | 1 | $100 | Replaces the guesses in src/herdtag/power.py |
| Digital calipers | 1 | $25 | ||
| USB-C data cables | 4 | $20 | Data, not charge-only | |
| JST PH crimp tool + housings | Engineer PA-09 or similar | 1 | $40 | To re-pin LiPos with reversed polarity |
| Conformal coating | MG Chemicals 422C (silicone) | 1 | $20 | Mask the connectors, U.FL, GNSS module, and piezo |
| Silicone O-ring cord, 1.5 mm | 2 m | $10 | Glue into a ring with cyanoacrylate | |
| Silicone grease + RTV silicone | 1 each | $15 | Grease for the gasket; RTV to seal the piezo | |
| Acoustic vent membrane, adhesive, 10 mm | e.g. Gore/Saati-style sticker | 15 | $20 VERIFY | Or skip; the piezo itself is IP67 |
| M2 × 8 screws, M2 heat-set inserts, M3 inserts, M3 × 10 bolts + nyloc | kit | 1 | $25 | Stainless |
| Potting compound (optional) | 2-part polyurethane | 1 | $30 | Only after the design freezes |
| Silica gel packets, small | 50 | $8 | One per case | |
| SPL meter (or calibrated phone app) | 1 | $30 | For the cue loudness cap | |
| PETG filament, 1 kg + SLA tough resin | 1 + 1 L | $22 + ~$60 | Ask what resin the makerspace stocks | |
| Subtotal | ~$425 |
5f. Mounting
| Item | Part number | Qty | Est. | Notes |
|---|---|---|---|---|
| Large blank visual ear tags (female panel + male button) | Allflex Global large blank, or Z Tags large (VERIFY SKU) | 25 | $30 | The case bolts to the panel with 2 × M3 |
| Tag applicator matching that brand | Allflex universal | 1 | $25 | The vet or experienced handler applies |
| Subtotal | ~$55 |
Grand total (estimate)
| Per-tag electronics | $909 |
| Cue board | $123 |
| Power | $122 |
| Gateway | $245 |
| Tools and consumables | $425 |
| Mounting | $55 |
| Sum | ~$1,880 |
| Shipping, tax, 10% contingency | ~$250 |
| All-in | ≈ $2,100 (≈ $1,750 if the makerspace has the tools) |
The cost of goods per tag is about $95. That's prototype pricing on dev modules; the v2 target is under $50 BOM.
6. Makerspace sessions
Session 1: bring up ONE tag (about 4 h)
Tools: soldering station, multimeter, PPK2, laptop with Arduino IDE / PlatformIO + RAK nRF52 BSP, calipers.
- [ ] Antennas on BEFORE first power. LoRa on the IPEX marked LoRa, BLE on BLE, GNSS patch on the RAK12500. Never let the SX1262 transmit without an antenna; it can damage the PA.
- [ ] Seat the RAK4631 on the CPU slot, the RAK12500 on slot A, and the RAK1904 on slot C. Use the M1.2 screws.
- [ ] Check the LiPo polarity against the RAK19007 silkscreen with a meter. If it's reversed, re-pin it. Don't trust the wire colors.
- [ ] Flash a blink, then an I2C scan. You should see 0x42 (GNSS) and 0x18/0x19 (accel).
- [ ] Build the cue board. Run the piezo at 4.25 kHz and measure the dB at 10 cm. Test the motor.
- [ ] Flash the
firmware/tag build (see the firmware README) with keys from TTS (section 7). - [ ] GNSS outdoors with open sky. A cold start can take a minute or more. Indoors it may never fix.
- [ ] OTAA join on US915 FSB2 and confirm the 11-byte uplink reaches the backend.
- [ ] PPK2 (source-meter mode at 3.7 V into the battery connector): record sleep current, one GNSS fix (energy and duration), and one uplink (energy). Put the numbers into
src/herdtag/power.py.
Pass when:
- Join succeeds.
- The uplink decodes correctly.
- The hot fix takes 5 s or less.
- Sleep current is 50 µA or less (target is closer to 20 µA). If it's higher, find the leak before building 11 more.
- The battery ADC reads within 50 mV of the multimeter.
Pitfalls:
- A device on the wrong sub-band never joins. That means FSB2 has to be set in both the firmware and the gateway.
- The base board charger reads USB-attached voltage, not the battery's.
- Some cheap LiPos lack a PCM. Don't use those.
Session 2: build the other 11 (about 5–6 h)
- [ ] Assembly line: 11 cue boards first, then the stacks, then the antennas, then polarity checks, then flashing.
- [ ] Label each tag with a number that matches its TTS device ID (
herdtag-01…herdtag-12). - [ ] Run the Session 1 checks on each one: I2C scan, cue, join, uplink, a 10-minute sleep-current check.
- [ ] Conformal-coat the cue boards. Mask the connectors.
Pass when 12 out of 12 join and uplink. Any board failing the sleep current check gets set aside as a spare or for debugging.
Session 3: enclosures, sealing, abuse tests (about 4 h plus printing beforehand)
- [ ] Measure the real stack height, piezo, and battery. Edit
enclosure/tag_case.scadand print one base and lid in PETG to fit-check. - [ ] Fit-check, adjust, then print 12 sets in SLA tough resin for anything going on an animal. PETG is fine for walk tests.
- [ ] Set the heat-set inserts. Fit the O-ring cord with grease. Seal the piezo with RTV. Add a silica gel packet.
- [ ] Soak test: a case with tissue paper inside, 30 min at 30 cm depth. Pass if the tissue is dry.
- [ ] Drop test: 1.5 m onto concrete, 6 faces. Pass if it's still working and still sealed.
- [ ] Cold test: freezer overnight at about -15 °C, then a join plus a fix. Note that LiPos must not charge below 0 °C. Flag this for the firmware and the winter field plan.
- [ ] Weigh the complete tag, including the ear-tag panel. Target is under 60 g.
Session 4: walk tests, no cattle (half a day, outdoors at the farm or a park)
- [ ] Mount the gateway as high as possible. Draw a test pasture in the backend and push it to the tags.
- [ ] Static accuracy: 3 tags on a fence post for 30 min. Compute the p50/p95 error and compare it to the sim's 2.5 / 5.3 m assumption.
- [ ] Coverage: walk the farm boundary with 2 tags and log uplink delivery %. Pass at 95% or better.
- [ ] Cue timing: a person walks at grazing pace and at walking pace toward the fence. The cue should fire in the warning zone, and stop on a turn or when the person backs away.
- [ ] Log everything and fit
GpsModelinsrc/herdtag/sim.pyto the real error data.
7. The Things Stack Sandbox + gateway setup
- Create an account on The Things Stack Sandbox, cluster nam1 (
nam1.cloud.thethings.network). - Gateway: Console → Gateways → Register. Enter the EUI from the RAK7268V2's web UI. Set the frequency plan to "United States 902–928 MHz, FSB 2 (used by TTN)".
- On the gateway's WisGate OS: LoRa → work mode Packet Forwarder → Basic Station (or Semtech UDP) → server
nam1.cloud.thethings.network. Set region US915 and sub-band 2 (channels 8–15, plus 65 for 500 kHz). - Application: create
herdtag. Register end devices manually: LoRaWAN 1.0.3, regional parameters RP001 1.0.3 rev A, same FSB2 plan, OTAA, JoinEUI0000000000000000, DevEUI and AppKey generated. Bulk-register 12 with the CLI:bash ttn-lw-cli login for i in $(seq -w 1 12); do ttn-lw-cli end-devices create herdtag herdtag-$i \ --dev-eui $(openssl rand -hex 8) --join-eui 0000000000000000 \ --frequency-plan-id US_902_928_FSB_2 --lorawan-version 1.0.3 \ --lorawan-phy-version 1.0.3-a --with-root-keys done ttn-lw-cli end-devices list herdtag # copy DevEUI/AppKey into each tag's firmware - Uplink webhook: Application → Integrations → Webhooks → Custom webhook.
- Format: JSON.
- Base URL:
https://<backend-host>. - Enable Uplink message with path/ingest/ttn. - The backend reads the standard TTS v3 uplink JSON (uplink_message.frm_payload, base64, 11 bytes on FPort 1). - No payload formatter is needed; the backend decodes the bytes itself. - Add a shared-secret header, and have the backend check it. - Fence downlinks: create an API key with "write downlink" rights.
- The backend queues fragments on FPort 2 through
POST /api/v3/as/applications/herdtag/webhooks/<id>/devices/<dev>/down/push. - Tags are Class A, so a downlink only arrives right after an uplink, one fragment per uplink. - The firmware should send a short follow-up uplink while the network reports more downlinks pending (the frame-pending bit), so a fence doesn't take hours to arrive. - 51-byte fragments fit the US915 RX1/RX2 data rates.
8. First cattle fitting: safety and welfare (Phase 2, not before Sessions 1–4 pass)
- A vet or experienced handler applies the tag, in the middle third of the ear between the cartilage ribs. Keep clear of the official ID tag. Don't remove or replace official ID.
- Weight under 60 g on the animal. Inspect the ears daily for 14 days, then twice a week. Remove the tag at any sign of infection or tearing.
- Sound cap: set a maximum SPL at 10 cm with the vet and behavior specialist, measured with the meter. Start low. The firmware caps bursts per episode and per hour; never escalate by volume.
- Only inside an existing physical fence, with no roads or hazards reachable. A handler is present for the first sessions.
- No electrical stimulus in v1. This hardware has none.
- Pre-register the trial thresholds (see
PLAN.mdPhase 2) before the first animal is tagged.
9. Path to v2 (after Phase 2 data)
- Custom 4-layer PCB: nRF52840 or nRF54L + SX1262 (or a combined module like RAK3172/STM32WL), u-blox MAX-M10S (25 mW tracking), LIS2DW12 accelerometer, an on-board piezo driver, and a solar MPPT/charger IC (e.g. BQ25570-class).
- Chip antennas, and a 300–500 mAh pouch cell shaped to the tag.
- Target ≤ 40 g, about 7 × 4 × 2.5 cm (Cornell V2 scale), in injection-moulded nylon. That gets the BOM under $50 at volume.
- Redesign the cue around whatever stimulus the Phase 2 data says works. That includes a two-tag, left/right option for herding.