Wearable Health Patch


Prepared the PCBWay DFM submission materials and regenerated the STEP model. I can’t submit to PCBWay directly from Flux, but these files are ready for you to upload to PCBWay’s rigid-flex/HDI quote channel for engineering feedback.
Project files:
Upload package:
Open items:
  • Final assembly is still incomplete: DRC has unresolved airwires.
  • Use these files for PCBWay engineering review only, not fabrication release.
  • After PCBWay provides the approved stackup/via table, manually finish AD5940/ADS1292 fanout, clear airwires, rerun DRC, then regenerate final exports.
I’ll inspect the current PCB state, determine whether placement, routing, or both need work, then run the appropriate auto-layout operations and verify the applied result.
Blocked: your account currently has no available ACUs, so AI inspection and auto-layout operations are unavailable.
Current known project state:
  • 6-layer rigid-flex working layout.
  • AD5940 support placement previously reorganized.
  • PCBWay HDI-oriented rules applied.
  • Last verified state had 24 unresolved airwires.
No PCB changes were made in this session. Remaining work is to inspect the latest board state, determine whether placement and/or routing is needed, run auto-layout, apply the result, and verify DRC once ACUs are available.
Check ACU balance
List airwires
Check DRC/ERC
Manual placement review

Firmware Starter — MDBT50Q-1MV2 / nRF52840
Platform & Toolchain
  • MCU module: Raytac MDBT50Q-1MV2, Nordic nRF52840
  • Framework: Arduino for prototyping; migrate to Zephyr/Nordic Connect SDK for regulated product firmware.
  • Build system: PlatformIO
Pin Mapping

Table


FunctionnRF52840 PinNetConnected To
I2C SCLP0.11I2C_SCLMAX30102, MPRLS, KX022, MAX17048
I2C SDAP0.12I2C_SDAMAX30102, MPRLS, KX022, MAX17048
SPI SCLKP0.13SPI_SCLKADS1292R, AD5940
SPI MOSIP0.14SPI_MOSIADS1292R DIN, AD5940 MOSI
SPI MISOP0.15SPI_MISOADS1292R DOUT, AD5940 MISO
ECG CSP0.16CS_ECG_NADS1292R CS
Electrochemical CSP0.17CS_ECHEM_NAD5940 CS
MCU resetP0.18MCU_RESET_NReset switch and SWD reset
ECG data readyP0.19ECG_DRDY_NADS1292R DRDY
ECG resetP0.20ECG_RESET_NADS1292R PWDN/RESET
ECG startP0.21ECG_STARTADS1292R START
Accelerometer CSP0.22ACCEL_CS_NKX022 CS, held high for I2C mode
AD5940 resetP0.23ECHEM_RESET_NAD5940 RESET
AD5940 interruptP0.24ECHEM_INTAD5940 GPIO0
PPG interruptP0.25PPG_INT_NMAX30102 INT
Pressure EOCP0.26PRESS_EOCMPRLS EOC
Accelerometer INT1P0.27ACCEL_INT1KX022 INT1
Fuel gauge alertP0.28FUEL_ALRT_NMAX17048 ALRT
Charger PGOODP0.30CHG_PGOOD_NBQ24074 PGOOD
Charger statusP0.31CHG_STAT_NBQ24074 CHG
User/DFU buttonP1.00USER_DFU_BTNSW2
Patient switch controlP1.01PATIENT_SW_CTRLADG824 IN pins
USBD+/D−/VBUSUSB_D_P/N, USB_VBUSUSB-C connector
SWDSWDIO/SWDCLKSWDIO/SWDCLKJ5
platformio.ini

Ini


[env:nrf52840_dk]
platform = nordicnrf52
board = nrf52840_dk
framework = arduino
monitor_speed = 115200
lib_deps =
  adafruit/Adafruit MAX3010x Sensor Library
  adafruit/Adafruit BusIO
Complete Starter Source

Cpp


#include <Arduino.h>
#include <Wire.h>
#include <SPI.h>
#include <bluefruit.h>

#define PIN_I2C_SCL          11
#define PIN_I2C_SDA          12
#define PIN_SPI_SCLK         13
#define PIN_SPI_MOSI         14
#define PIN_SPI_MISO         15
#define PIN_CS_ECG           16
#define PIN_CS_ECHEM         17
#define PIN_ECG_DRDY         19
#define PIN_ECG_RESET        20
#define PIN_ECG_START        21
#define PIN_ACCEL_CS         22
#define PIN_ECHEM_RESET      23
#define PIN_ECHEM_INT        24
#define PIN_PPG_INT          25
#define PIN_PRESS_EOC        26
#define PIN_ACCEL_INT1       27
#define PIN_FUEL_ALRT        28
#define PIN_CHG_PGOOD        30
#define PIN_CHG_STAT         31
#define PIN_USER_DFU         32   // P1.00 in schematic; map to Arduino variant as needed
#define PIN_PATIENT_SW_CTRL  33   // P1.01 in schematic; map to Arduino variant as needed

#define I2C_ADDR_MAX30102    0x57
#define I2C_ADDR_MPRLS       0x18
#define I2C_ADDR_MAX17048    0x36
#define I2C_ADDR_KX022_ALT0  0x1E

static BLEService vitalService = BLEService(0x181C);
static BLECharacteristic dataChar = BLECharacteristic(0x2A58);

uint8_t i2cRead8(uint8_t addr, uint8_t reg) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  if (Wire.endTransmission(false) != 0) return 0xFF;
  Wire.requestFrom(addr, (uint8_t)1);
  return Wire.available() ? Wire.read() : 0xFF;
}

bool i2cWrite8(uint8_t addr, uint8_t reg, uint8_t value) {
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.write(value);
  return Wire.endTransmission() == 0;
}

uint16_t max17048ReadReg(uint8_t reg) {
  Wire.beginTransmission(I2C_ADDR_MAX17048);
  Wire.write(reg);
  if (Wire.endTransmission(false) != 0) return 0xFFFF;
  Wire.requestFrom(I2C_ADDR_MAX17048, (uint8_t)2);
  if (Wire.available() < 2) return 0xFFFF;
  return ((uint16_t)Wire.read() << 8) | Wire.read();
}

void ecgWriteReg(uint8_t reg, uint8_t value) {
  digitalWrite(PIN_CS_ECG, LOW);
  SPI.transfer(0x40 | reg);   // WREG command base for ADS129x
  SPI.transfer(0x00);         // one register
  SPI.transfer(value);
  digitalWrite(PIN_CS_ECG, HIGH);
}

uint8_t ecgReadReg(uint8_t reg) {
  digitalWrite(PIN_CS_ECG, LOW);
  SPI.transfer(0x20 | reg);   // RREG command base
  SPI.transfer(0x00);
  uint8_t v = SPI.transfer(0x00);
  digitalWrite(PIN_CS_ECG, HIGH);
  return v;
}

uint16_t ad5940Read16(uint16_t addr) {
  digitalWrite(PIN_CS_ECHEM, LOW);
  SPI.transfer(0x6D);         // AD5940 read-register command placeholder; verify with ADI driver
  SPI.transfer((addr >> 8) & 0xFF);
  SPI.transfer(addr & 0xFF);
  uint16_t v = ((uint16_t)SPI.transfer(0x00) << 8) | SPI.transfer(0x00);
  digitalWrite(PIN_CS_ECHEM, HIGH);
  return v;
}

void initBle() {
  Bluefruit.begin();
  Bluefruit.setName("NeuraVitalPatch");
  vitalService.begin();
  dataChar.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
  dataChar.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
  dataChar.setFixedLen(20);
  dataChar.begin();
  Bluefruit.Advertising.addService(vitalService);
  Bluefruit.Advertising.addName();
  Bluefruit.Advertising.start(0);
}

void setPatientSensingEnabled(bool enabled) {
  // Fail-safe policy: keep disabled whenever USB/charge is present.
  bool usbPresent = digitalRead(PIN_CHG_PGOOD) == LOW;
  digitalWrite(PIN_PATIENT_SW_CTRL, (enabled && !usbPresent) ? HIGH : LOW);
}

void setup() {
  pinMode(PIN_CS_ECG, OUTPUT); digitalWrite(PIN_CS_ECG, HIGH);
  pinMode(PIN_CS_ECHEM, OUTPUT); digitalWrite(PIN_CS_ECHEM, HIGH);
  pinMode(PIN_ACCEL_CS, OUTPUT); digitalWrite(PIN_ACCEL_CS, HIGH);
  pinMode(PIN_ECG_RESET, OUTPUT); digitalWrite(PIN_ECG_RESET, HIGH);
  pinMode(PIN_ECG_START, OUTPUT); digitalWrite(PIN_ECG_START, LOW);
  pinMode(PIN_ECHEM_RESET, OUTPUT); digitalWrite(PIN_ECHEM_RESET, HIGH);
  pinMode(PIN_PATIENT_SW_CTRL, OUTPUT); digitalWrite(PIN_PATIENT_SW_CTRL, LOW);

  pinMode(PIN_ECG_DRDY, INPUT_PULLUP);
  pinMode(PIN_PPG_INT, INPUT_PULLUP);
  pinMode(PIN_PRESS_EOC, INPUT);
  pinMode(PIN_ACCEL_INT1, INPUT);
  pinMode(PIN_FUEL_ALRT, INPUT_PULLUP);
  pinMode(PIN_CHG_PGOOD, INPUT_PULLUP);
  pinMode(PIN_CHG_STAT, INPUT_PULLUP);
  pinMode(PIN_USER_DFU, INPUT_PULLUP);

  Serial.begin(115200);
  Wire.setPins(PIN_I2C_SDA, PIN_I2C_SCL);
  Wire.begin();
  SPI.begin();

  initBle();
  setPatientSensingEnabled(false);

  // Minimal sensor presence reads.
  Serial.printf("MAX30102 PART_ID: 0x%02X\n", i2cRead8(I2C_ADDR_MAX30102, 0xFF));
  Serial.printf("MAX17048 VCELL: 0x%04X\n", max17048ReadReg(0x02));
  Serial.printf("ADS1292 ID: 0x%02X\n", ecgReadReg(0x00));
}

void loop() {
  static uint32_t last = 0;
  if (millis() - last >= 1000) {
    last = millis();
    setPatientSensingEnabled(true);
    uint16_t vcell = max17048ReadReg(0x02);
    uint8_t ppgPart = i2cRead8(I2C_ADDR_MAX30102, 0xFF);
    char payload[20];
    snprintf(payload, sizeof(payload), "B%04X P%02X", vcell, ppgPart);
    dataChar.notify((uint8_t*)payload, strlen(payload));
    Serial.println(payload);
  }
}
Build & Flash

Bash


pio run
pio run -t upload
pio device monitor
Production Firmware Notes
  • Replace Arduino prototype with Nordic Connect SDK/Zephyr for production-intent medical software.
  • Implement secure BLE pairing, signed DFU, debug lockout, data integrity, audit logs, and safety state machine.
  • Verify AD5940 command/register access using ADI's official driver before relying on electrochemical data.
  • Patient sensing must default disabled at boot and whenever USB/charging is detected.
  • Platform & Toolchain

  • Pin Mapping

  • platformio.ini

  • Complete Starter Source

  • Build & Flash

  • Production Firmware Notes