================================================================================ ESP32-S3 → CR1000X ANGLE OUTPUT OPTIONS Project: SlowSweep Turntable | Written: 2026-07-02 ================================================================================ SOURCE OF TRUTH --------------- The ESP32-S3 firmware tracks rotation angle as a float (0.0–360.0°) computed from the stepper step counter (14400 steps/rev → 0.025°/step resolution). That value is the number to get into the CR1000X. NOTE: The ESP32-S3 has NO built-in DAC (unlike the original ESP32). "True analog" output requires either an RC filter on PWM or an external DAC chip. CR1000X QUICK REFERENCE (from manual §18.5.1 / §18.7) ------------------------------------------------------ SE analog inputs (SE1–SE16): - Voltage range: ±5 V (sustained ±20 V without damage) - Input impedance: 20 GΩ (no loading concern at all) - Resolution at ±5000 mV range: 11.8 µV RMS (19-bit effective) - Accuracy: ±(0.04% of reading + 2 µV offset) @ 15 kHz notch - CRBasic instruction: VoltSE() C terminals (C1–C8): - LVTTL logic: high ≥ 2.5 V, low ≤ 0.8 V ← ESP32-S3 3.3V GPIO works directly - C5 / C7 only: RS-232 capable (on CR1000X, not Xe) - All C terminals support: UART, SDI-12, edge counting, PWM period-avg, quadrature - CRBasic UART instruction: SerialOpen() / SerialIn() ================================================================================ OPTION 1 — PWM + RC LOW-PASS FILTER [RECOMMENDED] ================================================================================ Parts needed: 1× 10 kΩ resistor + 1× 47 µF capacitor (~$0.25, on hand likely) How it works: ESP32-S3 LEDC (PWM) GPIO ──[10 kΩ]──┬── SE analog input (e.g. SE1) │ 47 µF │ GND (signal ground on CR1000X) Duty cycle maps linearly to voltage: 0% duty = 0 V, 100% duty = 3.3 V Map 0°–360° → 0–100% duty → 0–3.3 V output ESP32-S3 firmware change: // In setup(): ledcAttachPin(OUTPUT_PIN, 0); // channel 0 ledcSetup(0, 10000, 12); // 10 kHz, 12-bit (0–4095) // Wherever angle is calculated: float angle_deg = (float)stepsInPass * 360.0f / STEPS_PER_REV; uint32_t duty = (uint32_t)(angle_deg / 360.0f * 4095); ledcWrite(0, duty); CRBasic (CR1000X program): Dim angle_volts As Float Dim angle_deg As Float VoltSE(angle_volts, 1, mV5000, 1, True, 0, 60, 1.0, 0) 'SE1, ±5000mV range angle_deg = angle_volts / 3300.0 * 360.0 '0–3300 mV → 0–360° Performance: - RC time constant τ = 10 kΩ × 47 µF = 470 ms (settles in ~2.5 s) - At 10 kHz PWM: residual ripple ≈ 0.5 mV → ~0.05° of noise on CR1000X - Angular resolution from 12-bit PWM: 360° / 4096 = 0.088° per step - CR1000X voltage resolution (11.8 µV) is far finer — PWM is the limiting factor Pros: True analog voltage. Standard VoltSE() measurement, no special CRBasic. Zero firmware complexity on CR1000X side. Cons: 2 passive components. Slow settling (not a problem for slow turntable). ================================================================================ OPTION 2 — SERIAL ASCII OVER UART [ZERO NEW PARTS — best for data logging] ================================================================================ Parts needed: 1 wire (ESP32 TX → CR1000X C5 or C7) GND wire (shared ground) How it works: ESP32-S3 UART TX (3.3V, LVTTL) connects directly to C5 or C7 (RS-232-capable C terminals). CR1000X can receive at RS-232 voltages OR LVTTL — the spec confirms C terminals accept ≥2.5 V as logic high, so 3.3V TX works directly WITHOUT a level converter. ESP32 sends a line once per second: "ANGLE=123.4\r\n" ESP32-S3 firmware change: // In setup(): Serial1.begin(9600, SERIAL_8N1, -1, OUTPUT_TX_PIN); // TX only // In loop(), once per second: Serial1.printf("ANGLE=%.1f\r\n", angle_deg); CRBasic (CR1000X program): Dim angle_str As String * 20 Dim angle_deg As Float SerialOpen(ComC5, 9600, 0, 0, 50) SerialIn(angle_str, ComC5, 100, 13, 20) 'wait for CR angle_deg = Mid(angle_str, 7, 5) 'parse after "ANGLE=" Pros: Zero extra hardware. Lossless — exact floating-point angle, no conversion. Can send additional fields (direction, state, speed) in same message. Cons: Requires CRBasic string parsing. Uses one C terminal permanently. CR1000X serial port must not conflict with other instruments. ================================================================================ OPTION 3 — PWM PERIOD AVERAGING [ZERO NEW PARTS] ================================================================================ Parts needed: 1 wire (ESP32 GPIO → CR1000X C terminal) How it works: ESP32 outputs a PWM signal whose frequency (or period) encodes the angle. CR1000X uses PeriodAvg() to measure the period in microseconds, then CRBasic math converts to degrees. Encoding: frequency = (angle_deg / 360.0) × (f_max - f_min) + f_min Example: 100 Hz = 0°, 460 Hz = 360° (1 Hz per degree offset from 100) ESP32-S3 firmware: use LEDC to output variable frequency (change timer config) or use a software timer to toggle a GPIO at the target half-period. CRBasic: Dim period_us As Float Dim angle_deg As Float PeriodAvg(period_us, 1, C1, 10, 0, 0) '10-cycle average on C1 angle_deg = (1000000.0/period_us - 100) * 1.0 'convert Hz → degrees Performance: - Period resolution: ±0.13 µs / cycles averaged - At 10-cycle average, 300 Hz signal: 300 µs period → ±0.13/10 µs = ±0.04° - Good resolution, but frequency-to-angle math adds complexity Pros: No new hardware. Noise-immune (frequency encoding ignores amplitude noise). Cons: More complex firmware + CRBasic math. Frequency range must be planned. C terminal occupied permanently. ================================================================================ OPTION 4 — MCP4725 I²C DAC MODULE [BEST ANALOG ACCURACY] ================================================================================ Parts needed: MCP4725 breakout module (~$2–4, Adafruit #935 or clone) How it works: ESP32-S3 I²C → MCP4725 12-bit DAC → true analog 0–3.3 V (or 0–5 V if powered from 5V rail) → SE analog input on CR1000X. The MCP4725 produces a real DC voltage with no ripple — clean signal. ESP32-S3 firmware change: #include // MCP4725 I2C address 0x60 void setDAC(float angle_deg) { uint16_t val = (uint16_t)(angle_deg / 360.0f * 4095); Wire.beginTransmission(0x60); Wire.write(0x40); // fast write command Wire.write(val >> 4); Wire.write((val & 0xF) << 4); Wire.endTransmission(); } CRBasic: same as Option 1 (VoltSE), identical from CR1000X's perspective. Performance: - Resolution: 3300 mV / 4096 = 0.8 mV per LSB → ~0.09° per LSB - No ripple — true DC output - Update rate: I²C at 400 kHz → updates in <1 ms Pros: Cleanest analog signal. Zero PWM ripple. Standard VoltSE() on CR1000X. 5V power gives 0–5V output → uses more of CR1000X's ±5V input range. Cons: External module required ($2–4). I²C wiring (SDA + SCL + power + GND). ================================================================================ OPTION 5 — SDI-12 SMART SENSOR [MOST SCALABLE] ================================================================================ Parts needed: 1 wire + possibly a 5V level shifter How it works: SDI-12 is a 1200-baud half-duplex protocol used by environmental sensors. CR1000X C terminals support SDI-12 natively (SDI12Recorder() instruction). ESP32-S3 implements an SDI-12 "slave" that responds to CR1000X polling. SDI-12 uses 5V logic (high = 0V, low = 5V, inverted). The CR1000X C terminals have a 100 kΩ pull-up to 5V. This MAY work at 3.3V but is marginal — a simple NPN transistor or 74HCT gate for level shift is safer. ESP32 uses an SDI-12 library (e.g. EnviroDIY/Arduino-SDI-12). CR1000X polls with SDI12Recorder() — gets angle as a labeled float. Pros: Industry-standard sensor protocol. Multiple values in one transaction (angle + direction + state). CR1000X can auto-timestamp and log. Cons: More complex firmware. Level shifting likely needed. Overkill for one value. ================================================================================ RECOMMENDATION SUMMARY ================================================================================ FOR FASTEST DEPLOYMENT: Option 2 (Serial UART) — plug in 2 wires, add ~10 lines of CRBasic. Exact angle value, no hardware, done in an afternoon. FOR CLEANEST ANALOG SIGNAL (customer expects a "voltage input"): Option 1 (PWM + RC filter) — $0.25 in parts, standard VoltSE() in CRBasic, ~0.1° resolution. Most familiar to field technicians used to analog sensors. FOR BEST OVERALL (if you have a spare I²C channel): Option 4 (MCP4725 DAC) — $3 module, ripple-free true analog, identical CRBasic to Option 1 but no RC settling delay. WIRING NOTE (all options): Always connect ESP32-S3 GND to a Signal Ground (±) terminal on the CR1000X. Do NOT use Power Ground (G) as reference for analog inputs — the manual specifies Signal Ground as the reference for SE measurements. ================================================================================