old front end cct with z80 spi oled display - SteveJustin1963/radio-fax-Hellschreiber GitHub Wiki

The PDF titled "Home-built Hellschreibers" by F. Dörenberg is an exhaustive 51-page archive documenting over 20 mechanical and electronic Hellschreiber machines built by amateur radio enthusiasts since the late 1970s. Here’s a structured summary of the key content:


🧾 Overview

  • Focuses on homebuilt Hellschreiber printers and senders.
  • Includes original builds, construction tips, schematics, motor control circuits, audio filters, and references.

🛠️ Key Technical Highlights

1. Author’s Own Build (1983)

  • CNC-cut double-sided PCB structure, compact size (~15x8x12 cm).

  • Solenoid printer with tone detection, servo motor for spindle, geared motor for paper transport.

  • Used Pelikan endorsing ink with a felt roller made from furniture pads.

  • Spindle: single-thread worm, needs 1050 rpm to match Feld-Hell output.

  • Paper transport: ~47 cm/min with 16 mm roller (~9.3 rpm).

  • Motor speed calibration done via:

    • Optical tachometer with IR sensor + strobe disk.
    • Measurement via oscilloscope.

2. Circuits

  • Solenoid driver and motor speed controller circuits included.
  • Tips for speeding up slow relays using temporary overvoltage tricks.

3. Printer Rollers

  • Homemade from felt pads or polishing wheels.
  • Ink must be non-water-based, to prevent rust and smearing.

🧑‍🔧 Examples of Other Builds

  • Mart PA0MJS: Built 3 mechanical printers (1979–1982), including one using a bicycle dynamo.
  • DG9JO: Used a cassette deck motor + flywheel.
  • PA0MPR & PA0ARB: Complex diode-matrix-driven sender, no rotating drum.
  • PA3AFD: Used threaded inserts for the spindle, cut to exactly 2 threads.
  • PA0ZR: Complete mobile station using BPSK and slow-Hell (20 baud), self-built CPU logic with ROM fonts.
  • VK2DSH (Australia): Combined 65C02 SBC with floppy drive parts.
  • ON4ASZ: Used squirrel-cage AC motor for tape transport, custom rheostat for control.
  • PA0WV: LCD-based digital version using AT90S8515 and Optrex 256x128 display.
  • PE1AQB: Modular unit with two interchangeable printer heads (one needle, one spindle).
  • VE6AQO: Optical encoder system, precision bearings, worm gears, PIC controller.
  • SQ3TCE: Almost fully 3D-printed unit including spindle.
  • PE1DCF: Uses adjustable zener diode snubbers for fine magnet timing.

🎚️ Filter Design

  • Ultra-narrow audio filter (-3 dB at 150 Hz, -60 dB at 680 Hz).
  • Uses 88 mH toroidal coils (originally telephone line inductors).
  • Alternatives: Toko 10RB inductors or active switched-capacitor filters (NESCAF, etc.).

📚 Reference Sections

  • Dozens of published articles in magazines like Electron, CQ-PA, and Funkamateur.
  • Multiple linked PDFs with schematics, filter designs, and character ROM data sheets.

Useful Links (included in the PDF)


Would you like help building one yourself, choosing a specific circuit to reproduce, or ordering the right parts (e.g. solenoid, spindle, filters)?

keep the front-end of the Hellschreiber (audio filter + tone detector) and replace the back-end (relay, spindle, paper) with a microcontroller that paints columns onto a display.

Below is a proven, simple path plus an all-digital alternative.


A. Minimal retrofit (reuse the analog circuits)

Signal path

  1. Audio in (from RX at ~900 Hz tone)
  2. Narrow AF filter (e.g., PA0KLS style, ~150 Hz BW) →
  3. Envelope + Schmitt (clean on/off) →
  4. MCU digital input (samples at the Hell pixel cadence) →
  5. Display driver (OLED/LCD/TFT).

Timing you need

  • Feld-Hell rate ≈ 2.5 chars/s, 98 pixels/char245 pixels/sTₚᵢₓ ≈ 4.08 ms per pixel.
  • One column = 14 pixelsTᶜₒₗ ≈ 57.1 ms (advance one display column each 57 ms). These values are exactly the ones used in the mechanical builds (column period ≈ 57 ms).

Hardware suggestion

  • MCU: RP2040 (Pico), ESP32, or an Arduino-class AVR (Timer-driven sampling).

  • Display:

    • 128×64 SSD1306 OLED (fits one or two 14-pixel text lines nicely), or
    • 240×135/240×320 ST7789 TFT (more columns/history).
  • Keep your existing AF filter and detector; feed its TTL-like output to an MCU pin with an interrupt or timer.

Drawing model (column writer)

  • Every Tₚᵢₓ ≈ 4.08 ms: sample detector → set one bit of a 14-bit column buffer (top→bottom).
  • After 14 samples (~57 ms), push that 14-pixel column to the display, then scroll 1 px to the left (or advance the write x).
  • For the classic Hell double-print look, draw the same column twice separated vertically by 1–2 pixels.

Why this works

  • Feld-Hell is just a timed stream of mark/space pixels; the mechanical helix implements the 14-pixel vertical sweep in 57 ms. You’re emulating that sweep in software and drawing it as a vertical column.

Calibration controls (put on two pots or UI sliders)

  • Pixel period trim (around 4.08 ms).
  • Threshold/AGC (so the Schmitt sees clean marks).
  • Optional: fine column-rate PLL that slowly nudges Tₚᵢₓ to keep lines horizontal over time.

B. All-digital front-end (no analog filter needed)

  1. Sample audio at 8–12 kHz (I²S/ADC).
  2. Apply a Goertzel or narrow IIR centered at 900 Hz.
  3. Envelope/AGC + comparator → boolean mark/space.
  4. Same column-writer as above.
  5. Add a light software PLL that locks the column period ≈ 57 ms by maximizing vertical correlation (keeps text level when RX drifts).

This gives you fewer parts and easy retuning if you later use Hell-80 or variants.


Display sizing (quick picks)

  • SSD1306 128×64:

    • Height fits two Hell lines (2×14 + spacing).
    • Width 128 columns ⇒ ≈ 18 characters across (since 7 columns/char).
  • ST7789 240×135: shows ≈ 34 characters across; comfortable for history.


Pseudocode skeleton (Arduino-style, timer driven)

// constants
const float Tpix_ms = 4.08;          // tweakable
const uint8_t PIXELS_PER_COL = 14;

volatile bool mark;                   // ISR-updated: detector state
uint16_t colBits = 0;                 // 14-bit column buffer
uint8_t  pixIdx = 0;
int x = 0;                            // current draw column

void IRAM_ATTR detectorISR() {
  // If you wire the detector to an interrupt pin, you may not need this;
  // simplest is polling in the timer tick. ISR shown for completeness.
}

void timerTick() { // called every ~4.08 ms
  colBits <<= 1;
  colBits |= (mark ? 1 : 0);
  pixIdx++;

  if (pixIdx == PIXELS_PER_COL) {
    drawColumn(x, colBits);           // write 14 vertical pixels
    drawColumn(x, colBits, 1);        // optional 2nd pass offset by 1 px
    x = (x + 1) % DISPLAY_WIDTH;      // or scroll when reaching edge
    pixIdx = 0;
    colBits = 0;
  }
}

void setup() {
  initDisplay();
  initDetectorInput();                // Schmitt output pin
  startTimerEvery(Tpix_ms, timerTick);
}

void loop() {
  mark = digitalRead(DETECTOR_PIN);   // or compute from Goertzel if all-digital
}

drawColumn(x, bits, yOffset=0) sets pixels (x, yOffset..yOffset+13) according to the 14 bits (top bit = top pixel). Add simple scrolling.


Parts list (example)

  • MCU Pico / ESP32 / Arduino Nano Every.
  • Display SSD1306 128×64 (I²C) or ST7789 240×135 (SPI).
  • Existing AF filter + detector from your Hell circuits (or do Goertzel).
  • Two trimmers (pixel-period and threshold) if you keep analog front-end.

Build/bring-up steps

  1. Get your detector producing a clean logic signal while listening to a Hell signal.
  2. Blink an LED at Tₚᵢₓ to verify timer accuracy.
  3. Print dummy test columns (e.g., alternating 0x1555/0x2AAA) to verify geometry.
  4. Feed real audio; trim Tₚᵢₓ until text sits horizontal.
  5. Add quality-of-life: brightness, contrast, invert, freeze, save bitmap.

If you want, I can provide:

  • A ready-to-flash RP2040/ESP32 sketch for SSD1306 or ST7789.
  • A small PLL that auto-locks to column rate so you rarely touch the timing.
  • A PCB that reuses your filter/detector header and powers the display/MCU.

code

; Adapted Z80 Bit-Banged SPI Code for SSD1306 OLED Display as Hellschreiber Printer
;
; Assumes you already have a Hellschreiber signal detector that outputs TTL pulses
; representing mark/space pixels at ~4ms per pixel (245 Hz), and a 14-pixel column
; is completed every 57ms. This code draws vertical columns on the OLED.

; --- IO PORT DEFINITIONS ---
PORT_MOSI   EQU 0x10
PORT_SCLK   EQU 0x11
PORT_CS     EQU 0x12
PORT_DC     EQU 0x13
PORT_RESET  EQU 0x14

; --- GENERAL REGISTERS ---
; A = SPI data byte
; B = bit loop counter
; C = temporary port value

; === SPI BIT-BANG SEND BYTE ===
; A = byte to send
SendSPIByte:
    LD B, 8
SendLoop:
    RLCA                  ; Rotate MSB into carry
    LD C, 0
    JP NC, MOSI_Low
MOSI_High:
    LD C, 1
MOSI_Low:
    OUT (PORT_MOSI), C

    ; Clock HIGH
    LD C, 1
    OUT (PORT_SCLK), C
    NOP
    NOP

    ; Clock LOW
    LD C, 0
    OUT (PORT_SCLK), C
    NOP
    NOP

    DJNZ SendLoop
    RET

; === SEND COMMAND BYTE ===
SendCommand:
    ; DC LOW
    LD C, 0
    OUT (PORT_DC), C
    CALL SendSPIByte
    RET

; === SEND DATA BYTE ===
SendData:
    ; DC HIGH
    LD C, 1
    OUT (PORT_DC), C
    CALL SendSPIByte
    RET

; === INITIALIZE OLED ===
InitOLED:
    ; Reset sequence
    LD C, 0
    OUT (PORT_RESET), C
    LD DE, 1000
WaitReset:
    DEC DE
    LD A, D
    OR E
    JP NZ, WaitReset

    LD C, 1
    OUT (PORT_RESET), C

    ; CS LOW
    LD C, 0
    OUT (PORT_CS), C

    ; Send basic SSD1306 init commands
    LD A, 0xAE      ; Display OFF
    CALL SendCommand

    ; ... insert rest of init sequence here ...

    LD A, 0xAF      ; Display ON
    CALL SendCommand

    ; CS HIGH
    LD C, 1
    OUT (PORT_CS), C
    RET

; === WRITE COLUMN DATA ===
; A = column data (bits 0–7 = vertical pixels)
; Assumes pre-set X/Y cursor
SendColumn:
    ; CS LOW
    LD C, 0
    OUT (PORT_CS), C
    CALL SendData
    ; CS HIGH
    LD C, 1
    OUT (PORT_CS), C
    RET

; === MAIN HELL LOOP (outline) ===
MainLoop:
    ; Wait for 4ms tick from external timer or interrupt (not shown)
    ; Sample detector pin (e.g. bit from port or memory-mapped input)
    ; Shift bit into a 14-bit buffer
    ; After 14 ticks, draw vertical line to OLED using SendColumn routine
    JP MainLoop

; Add routines to set column address, clear screen, scroll, etc.

; === END OF FILE ===