Devesh36/bicycle-brake-light

A USB‑C rechargeable 1S Li‑ion bicycle brake-light PCB with BQ24074 charging/protection, 3.3V AVR control, ADXL345 motion sensing, six PWM-driven red LEDs, and ISP programming.

Version
0.1.3
License
unset
Stars
0

index.circuit.tsx

PCBPCB preview for index.circuit.tsx
SchematicSchematic preview for index.circuit.tsx
import type { PinAttributeMap } from '@tscircuit/props'
/** Workaround for props requiring a total Record, including every alias.
 * Populate all keys explicitly so this helper does not just suppress a type error.
 */
function completePinAttributes<T extends Record<string, readonly string[]>>(
  labels: T,
  attributes: Partial<Record<keyof T, PinAttributeMap>>,
): Record<Extract<keyof T,string> | T[keyof T][number], PinAttributeMap> {
  return Object.fromEntries(Object.entries(labels).flatMap(([pin, aliases]) =>
    [pin, ...aliases].map(alias => [alias, attributes[pin] ?? {}])
  )) as Record<Extract<keyof T,string> | T[keyof T][number], PinAttributeMap>
}

import { Fragment } from "react"
import { BQ24074RGTR } from './imports/BQ24074RGTR'
import { TYPE_C_31_M_12 } from './imports/TYPE_C_31_M_12'
import { JstBattery } from './imports/JstBattery'
import { MSK12C02 } from './imports/MSK12C02'
import { AP2112K_3_3TRG1, pinLabels as AP2112K_3_3TRG1_pins } from './imports/AP2112K_3_3TRG1'
import { ATTINY85V_10SU, pinLabels as ATTINY85V_10SU_pins } from './imports/ATTINY85V_10SU'
import { ADXL345BCCZ_RL7, pinLabels as ADXL345BCCZ_RL7_pins } from './imports/ADXL345BCCZ_RL7'
import { XL_1608SURC_06 } from './imports/XL_1608SURC_06'

const n = (name: string) => `net.${name}`
const R = ({name, value, a, b, x, y}: {name:string,value:string,a:string,b:string,x:number,y:number}) =>
  <resistor name={name} resistance={value} footprint="0603" pcbX={x} pcbY={y} connections={{pin1:n(a),pin2:n(b)}} />
const C = ({name, value, rail, x, y}: {name:string,value:string,rail:string,x:number,y:number}) =>
  <capacitor name={name} capacitance={value} maxVoltageRating="10V" footprint="0603" pcbX={x} pcbY={y} connections={{pin1:n(rail),pin2:n('GND')}} />

export default () => <board width="90mm" height="45mm" thickness="1.6mm" minTraceWidth={0.15} minViaHoleDiameter={0.3} minViaPadDiameter={0.6} pcbStyle={{viaHoleDiameter:0.3,viaPadDiameter:0.6}} autorouter={{preset:"auto_local",traceClearance:0.15,allowViaInPad:false}}>
  <schematicsheet name="Power" displayName="USB-C charging and protected 1S battery" sheetIndex={1} sheetSize="ANSI_B">
    <group schGrid gridCols={4} gridGap={3}>
      <TYPE_C_31_M_12 name="J1" pcbX={-31} pcbY={-18} noConnect={["pin5","pin7","pin8","pin9","pin10","pin11"]}
        connections={{pin1:n('GND'),pin2:n('GND'),pin3:n('GND'),pin4:n('GND'),pin6:n('CC1'),pin12:n('CC2'),pin13:n('GND'),pin14:n('GND'),pin15:n('USB5V'),pin16:n('USB5V')}} />
      <R name="R1" value="5.1k" a="CC1" b="GND" x={-36} y={-10}/>
      <R name="R2" value="5.1k" a="CC2" b="GND" x={-31} y={-10}/>
      <C name="C1" value="4.7uF" rail="USB5V" x={-27} y={-12}/>
      <BQ24074RGTR name="U1" pcbX={-23} pcbY={-6} noConnect={["pin7","pin15"]}
        connections={{pin1:n('NTC'),pin2:n('VBAT'),pin3:n('VBAT'),pin4:n('GND'),pin5:n('VSYS'),pin6:n('GND'),pin8:n('GND'),pin9:n('CHG_N'),pin10:n('VSYS'),pin11:n('VSYS'),pin12:n('ILIM'),pin13:n('USB5V'),pin14:n('TMR'),pin16:n('ISET'),pin17:n('GND')}} />
      <JstBattery name="J2" pcbX={-33} pcbY={4} connections={{pin1:n('VBAT'),pin2:n('NTC'),pin3:n('GND')}} />
      <R name="R3" value="8.87k" a="ISET" b="GND" x={-27} y={-2}/>
      <R name="R4" value="6.49k" a="ILIM" b="GND" x={-19} y={-9}/>
      <R name="R5" value="68k" a="TMR" b="GND" x={-19} y={-3}/>
      <C name="C2" value="10uF" rail="VBAT" x={-24} y={3}/>
      <C name="C3" value="10uF" rail="VSYS" x={-18} y={1}/>
      <R name="R6" value="2.2k" a="USB5V" b="CHG_A" x={-36} y={-5}/>
      <XL_1608SURC_06 name="D7" color="red" pcbX={-40} pcbY={-5} connections={{anode:n('CHG_A'),cathode:n('CHG_N')}} />
      <MSK12C02 name="SW1" type="spdt" pcbX={-12} pcbY={-18} noConnect={["pin3"]} connections={{pin1:n('SW_VIN'),pin2:n('VSYS'),pin4:n('GND')}} />
      <AP2112K_3_3TRG1 pinAttributes={completePinAttributes(AP2112K_3_3TRG1_pins, {pin1:{requiresPower:true},pin2:{requiresGround:true},pin3:{isInput:true},pin5:{providesPower:true,providesVoltage:3.3}})} name="U2" pcbX={-10} pcbY={-7} noConnect={["pin4"]} connections={{pin1:n('SW_VIN'),pin2:n('GND'),pin3:n('SW_VIN'),pin5:n('V3V3')}} />
      <C name="C4" value="1uF" rail="SW_VIN" x={-14} y={-11}/>
      <C name="C5" value="1uF" rail="V3V3" x={-6} y={-11}/>
    </group>
  </schematicsheet>
  <schematicsheet name="Control" displayName="Motion sensing and programming" sheetIndex={2} sheetSize="ANSI_B">
    <group schGrid gridCols={4} gridGap={3}>
      <ATTINY85V_10SU pinAttributes={completePinAttributes(ATTINY85V_10SU_pins, {pin8:{requiresPower:true,requiresVoltage:3.3},pin4:{requiresGround:true},pin1:{isInput:true},pin2:{isInput:true},pin3:{isInput:true},pin5:{isBidirectional:true},pin6:{isOutput:true},pin7:{isBidirectional:true}})} name="U3" pcbX={0} pcbY={-4} connections={{pin1:n('RESET'),pin2:n('SUPPLY_SENSE'),pin3:n('INT1'),pin4:n('GND'),pin5:n('SDA'),pin6:n('LED_PWM'),pin7:n('SCL'),pin8:n('V3V3')}} />
      {/* ADXL registry aliases omit CS, SDO, SDA and SCL: use datasheet pin numbers. */}
      <ADXL345BCCZ_RL7 pinAttributes={completePinAttributes(ADXL345BCCZ_RL7_pins, {pin1:{requiresPower:true},pin6:{requiresPower:true},pin2:{requiresGround:true},pin4:{requiresGround:true},pin5:{requiresGround:true},pin13:{isBidirectional:true},pin14:{isInput:true}})} name="U4" pcbX={15} pcbY={-4} noConnect={["pin9","pin10"]} connections={{pin1:n('V3V3'),pin2:n('GND'),pin3:n('V3V3'),pin4:n('GND'),pin5:n('GND'),pin6:n('V3V3'),pin7:n('V3V3'),pin8:n('INT1'),pin11:n('GND'),pin12:n('GND'),pin13:n('SDA'),pin14:n('SCL')}} />
      <R name="R7" value="4.7k" a="V3V3" b="SDA" x={8} y={2}/>
      <R name="R8" value="4.7k" a="V3V3" b="SCL" x={13} y={2}/>
      <R name="R9" value="10k" a="V3V3" b="RESET" x={-5} y={-15}/>
      <R name="R10" value="220k" a="SW_VIN" b="SUPPLY_SENSE" x={3} y={-14}/>
      <R name="R11" value="100k" a="SUPPLY_SENSE" b="GND" x={8} y={-14}/>
      <C name="C6" value="100nF" rail="V3V3" x={-3} y={2}/>
      <C name="C7" value="100nF" rail="V3V3" x={12} y={-8}/>
      <C name="C8" value="100nF" rail="V3V3" x={18} y={-8}/>
      <C name="C9" value="1uF" rail="V3V3" x={20} y={-3}/>
      <C name="C10" value="10nF" rail="SUPPLY_SENSE" x={8} y={-18}/>
      <connector name="J3" manufacturerPartNumber="AVR-ISP-2x3-2.54mm" pcbX={30} pcbY={-14}
        pinLabels={{pin1:'MISO',pin2:'VCC',pin3:'SCK',pin4:'MOSI',pin5:'RESET',pin6:'GND'}}
        footprint={<footprint insertionDirection="from_above">{[1,2,3,4,5,6].map((p)=><Fragment key={p}><platedhole portHints={[`pin${p}`]} pcbX={((p-1)%2)*2.54-1.27} pcbY={2.54-Math.floor((p-1)/2)*2.54} holeDiameter={1} outerDiameter={1.8} shape="circle" /></Fragment>)}<courtyardrect width={5.5} height={8.1}/></footprint>}
        connections={{pin1:n('LED_PWM'),pin2:n('V3V3'),pin3:n('SCL'),pin4:n('SDA'),pin5:n('RESET'),pin6:n('GND')}} />
    </group>
  </schematicsheet>
  <schematicsheet name="Lights" displayName="Six red LEDs and PWM switch" sheetIndex={3} sheetSize="ANSI_B">
    <group schGrid gridCols={4} gridGap={3}>
      {[-30,-18,-6,6,18,30].flatMap((x,i)=>[
        <R key={`R${12+i}`} name={`R${12+i}`} value="120" a="V3V3" b={`LED${i+1}_A`} x={x} y={10}/>,
        <XL_1608SURC_06 key={`D${i+1}`} name={`D${i+1}`} color="red" pcbX={x} pcbY={15} connections={{anode:n(`LED${i+1}_A`),cathode:n('LED_RETURN')}} />
      ])}
      {/* Native transistor type=mosfet currently selects BJT ports; keep actual AO3400A physical mapping explicit. */}
      <chip pinAttributes={{pin1:{isInput:true},pin2:{requiresGround:true},pin3:{isPassive:true}}} name="Q1" manufacturerPartNumber="AO3400A" footprint="sot23" pcbX={29} pcbY={-3}
        pinLabels={{pin1:'GATE',pin2:'SOURCE',pin3:'DRAIN'}} connections={{pin1:n('GATE'),pin2:n('GND'),pin3:n('LED_RETURN')}} />
      <R name="R18" value="100" a="LED_PWM" b="GATE" x={25} y={-7}/>
      <R name="R19" value="100k" a="GATE" b="GND" x={33} y={-7}/>
    </group>
  </schematicsheet>
  <copperpour name="GroundTop" layer="top" connectsTo="net.GND" clearance={0.2} boardEdgeMargin={0.5} useThermalReliefs />
  <copperpour name="GroundBottom" layer="bottom" connectsTo="net.GND" clearance={0.2} boardEdgeMargin={0.5} useThermalReliefs />
  {[-40,40].flatMap(x=>[-17,17].map(y=><Fragment key={`${x},${y}`}><hole pcbX={x} pcbY={y} diameter="3.2mm" /></Fragment>))}
  <silkscreentext text="BICYCLE BRAKE LIGHT · REV A" pcbX={0} pcbY={20} fontSize={1.1}/>
  <silkscreentext text="FORWARD +X >" pcbX={15} pcbY={-11} fontSize={0.8}/>
  <silkscreentext text="1S + / NTC / -" pcbX={-33} pcbY={7} fontSize={0.7}/>
  <silkscreentext text="ISP 3.3V" pcbX={30} pcbY={-20} fontSize={0.8}/>
</board>