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)
- [Strobe disk generator PDF](https://www.nonstopsystems.com/radio/pdf-hell/strobe-disks.pdf)
- [Main Hellschreiber page](https://www.nonstopsystems.com/radio/hellschreiber-homebuilt.htm)
- [Filter circuits](https://www.nonstopsystems.com/radio/pdf-hell/article-hell-CW-filter.pdf)
- [Build examples and updates](https://www.nonstopsystems.com/radio/hellschreiber-page-updates.htm)
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
- Audio in (from RX at ~900 Hz tone) →
- Narrow AF filter (e.g., PA0KLS style, ~150 Hz BW) →
- Envelope + Schmitt (clean on/off) →
- MCU digital input (samples at the Hell pixel cadence) →
- Display driver (OLED/LCD/TFT).
Timing you need
- Feld-Hell rate ≈ 2.5 chars/s, 98 pixels/char ⇒ 245 pixels/s → Tₚᵢₓ ≈ 4.08 ms per pixel.
- One column = 14 pixels ⇒ Tᶜₒₗ ≈ 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)
- Sample audio at 8–12 kHz (I²S/ADC).
- Apply a Goertzel or narrow IIR centered at 900 Hz.
- Envelope/AGC + comparator → boolean mark/space.
- Same column-writer as above.
- 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
- Get your detector producing a clean logic signal while listening to a Hell signal.
- Blink an LED at Tₚᵢₓ to verify timer accuracy.
- Print dummy test columns (e.g., alternating 0x1555/0x2AAA) to verify geometry.
- Feed real audio; trim Tₚᵢₓ until text sits horizontal.
- 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 ===