Base para um projeto decodificador RTTY , utilizando tv antiga como terminal 


Segue um projeto em andamento, um decodificador RTTY , está implementado em um ESP32.

Tom de áudio em 2000Hz




Tons de áudio 2125Hz e 2295Hz





Tom de áudio 2295Hz



Sem áudio



Tom da áudio 2125Hz


O código

Biblioteca para a geração do vídeo composto Bitluni https://github.com/bitluni/ESP32Lib



#include <Arduino.h>
#include <ESP32Video.h>  // < ----Fonte https://github.com/bitluni/ESP32Lib //https://www.youtube.com/watch?v=-JXuwwXQh8c&list=PLjUbKCHhzPEzCm2_KFAICIN-7QTap_s72&index=4
#include <esp_task_wdt.h>

// --- HARDWARE ---
#define VIDEO_PIN 25
#define ADC_PIN   34

// --- RTTY SETTINGS ---
#define SAMPLE_RATE      8000   
#define BLOCK_SIZE       160    
const float targetFreqs[] = {2125.0, 2295.0}; 

CompositeGrayDAC display;
float coeffs[2];
float q1[2], q2[2];
int16_t samples[BLOCK_SIZE];

/* ================= FONTE 16x16 PARA RTTY ================= */
static const uint16_t font16x16_basic[128][16] = {
    {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},
    {0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x0180, 0x1DB8, 0x0FF0, 0x07E0, 0x03C0, 0x0180, 0x0000, 0x0000}, // LF
    {0},{0},
    {0x0000, 0x0000, 0x0000, 0x0000, 0x0070, 0x0038, 0x3FFF, 0x3FFF, 0x3838, 0x1C70, 0x0EE0, 0x07C0, 0x0380, 0x0000, 0x0000, 0x0000}, // CR
    {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, // SPACE
    {0x0180,0x03C0,0x03C0,0x03C0,0x03C0,0x0180,0x0180,0x0180,0x0180,0x0180,0x0000,0x0180,0x0180,0x0000,0x0000,0x0000}, // !
    {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},{0},
    {0x03C0,0x0FF0,0x1C38,0x381C,0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x381C,0x1C38,0x0FF0,0x03C0,0x0000,0x0000}, // 0
    {0x0180,0x0380,0x0780,0x0D80,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x1FF8,0x1FF8,0x0000,0x0000}, // 1
    {0x07E0,0x1FF8,0x381C,0x300C,0x001C,0x0038,0x0070,0x00E0,0x01C0,0x0380,0x0700,0x0E00,0x3FFC,0x3FFC,0x0000,0x0000}, // 2
    {0x07E0,0x1FF8,0x381C,0x300C,0x001C,0x03F0,0x03F0,0x001C,0x000C,0x300C,0x381C,0x1FF8,0x07E0,0x0000,0x0000,0x0000}, // 3
    {0x0030,0x0070,0x00F0,0x01B0,0x0330,0x0630,0x0C30,0x1830,0x3FFF,0x3FFF,0x0030,0x0030,0x0030,0x0030,0x0000,0x0000}, // 4
    {0x3FFC,0x3FFC,0x3000,0x3000,0x3FC0,0x3FE0,0x0070,0x0038,0x001C,0x001C,0x301C,0x1FF8,0x0FE0,0x0000,0x0000,0x0000}, // 5
    {0x01F0,0x07F8,0x0E1C,0x1C00,0x1C00,0x1BF0,0x1FF8,0x3C1C,0x380C,0x380C,0x3C1C,0x1FF8,0x0FF0,0x0000,0x0000,0x0000}, // 6
    {0x3FFF,0x3FFF,0x001C,0x0038,0x0070,0x00E0,0x01C0,0x0380,0x0700,0x0E00,0x1C00,0x1C00,0x1C00,0x1C00,0x0000,0x0000}, // 7
    {0x07E0,0x1FF8,0x381C,0x381C,0x1C38,0x0FF0,0x0FF0,0x1C38,0x381C,0x381C,0x381C,0x1FF8,0x07E0,0x0000,0x0000,0x0000}, // 8
    {0x0FF0,0x1FF8,0x381C,0x300C,0x300C,0x381C,0x1FFC,0x0FDC,0x001C,0x001C,0x3838,0x1FF0,0x0F80,0x0000,0x0000,0x0000}, // 9
    {0}, {0}, {0}, {0}, {0}, {0}, {0}, // Símbolos extras
    {0x03C0,0x0FF0,0x1C38,0x381C,0x381C,0x381C,0x3FFC,0x3FFC,0x381C,0x381C,0x381C,0x381C,0x381C,0x0000,0x0000,0x0000}, // A
    {0x3FF0,0x3FF8,0x381C,0x381C,0x381C,0x3FF8,0x3FF0,0x381C,0x381C,0x381C,0x381C,0x3FF8,0x3FF0,0x0000,0x0000,0x0000}, // B
    {0x03F0,0x0FFC,0x1C1C,0x3800,0x3000,0x3000,0x3000,0x3000,0x3000,0x3800,0x1C1C,0x0FFC,0x03F0,0x0000,0x0000,0x0000}, // C
    {0x3FE0,0x3FF8,0x381C,0x380E,0x380E,0x380E,0x380E,0x380E,0x380E,0x380E,0x381C,0x3FF8,0x3FE0,0x0000,0x0000,0x0000}, // D
    {0x3FFF,0x3FFF,0x3000,0x3000,0x3000,0x3FC0,0x3FC0,0x3000,0x3000,0x3000,0x3000,0x3FFF,0x3FFF,0x0000,0x0000,0x0000}, // E
    {0x3FFF,0x3FFF,0x3000,0x3000,0x3000,0x3FC0,0x3FC0,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000}, // F
    {0x03F0,0x0FFC,0x1C1C,0x3800,0x3000,0x3000,0x307E,0x307E,0x300E,0x380E,0x1C1C,0x0FFC,0x03F0,0x0000,0x0000,0x0000}, // G
    {0x381C,0x381C,0x381C,0x381C,0x381C,0x3FFC,0x3FFC,0x381C,0x381C,0x381C,0x381C,0x381C,0x381C,0x0000,0x0000,0x0000}, // H
    {0x1FF8,0x1FF8,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x1FF8,0x1FF8,0x0000,0x0000,0x0000}, // I
    {0x007E,0x007E,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x0018,0x1818,0x1C18,0x0FF8,0x07F0,0x0000,0x0000,0x0000}, // J
    {0x381C,0x3838,0x3870,0x38E0,0x39C0,0x3F80,0x3F80,0x39C0,0x38E0,0x3870,0x3838,0x381C,0x380E,0x0000,0x0000,0x0000}, // K
    {0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x3FFF,0x3FFF,0x0000,0x0000,0x0000}, // L
    {0xC003,0xE007,0xF00F,0xD81B,0xCC33,0xC663,0xC3C3,0xC183,0xC003,0xC003,0xC003,0xC003,0xC003,0x0000,0x0000,0x0000}, // M
    {0xC003,0xE003,0xF003,0xD803,0xCC03,0xC603,0xC303,0xC183,0xC0C3,0xC063,0xC033,0xC01B,0xC00F,0x0000,0x0000,0x0000}, // N
    {0x07E0,0x1FF8,0x381C,0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x381C,0x1FF8,0x07E0,0x0000,0x0000,0x0000}, // O
    {0x3FF0,0x3FF8,0x381C,0x381C,0x381C,0x3FF8,0x3FF0,0x3000,0x3000,0x3000,0x3000,0x3000,0x3000,0x0000,0x0000,0x0000}, // P
    {0x07E0,0x1FF8,0x381C,0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x306C,0x387C,0x1FF8,0x07E0,0x003C,0x003C,0x0000}, // Q
    {0x3FF0,0x3FF8,0x381C,0x381C,0x381C,0x3FF8,0x3FF0,0x38E0,0x3870,0x3838,0x381C,0x380E,0x3807,0x0000,0x0000,0x0000}, // R
    {0x07E0,0x1FF8,0x381C,0x300C,0x3000,0x1F80,0x07E0,0x003E,0x000C,0x300C,0x381C,0x1FF8,0x07E0,0x0000,0x0000,0x0000}, // S
    {0x7FFF,0x7FFF,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0000,0x0000,0x0000}, // T
    {0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x300C,0x381C,0x1FF8,0x07E0,0x0000,0x0000,0x0000}, // U
    {0x300C,0x300C,0x300C,0x1818,0x1818,0x0C30,0x0C30,0x0660,0x0660,0x03C0,0x03C0,0x0180,0x0180,0x0000,0x0000,0x0000}, // V
    {0xC003,0xC003,0xC003,0xC183,0xC3C3,0x6666,0x6666,0x3C3C,0x3C3C,0x1818,0x1818,0x1818,0x1818,0x0000,0x0000,0x0000}, // W
    {0x300C,0x1818,0x0C30,0x0660,0x03C0,0x0180,0x0180,0x03C0,0x0660,0x0C30,0x1818,0x300C,0x6006,0x0000,0x0000,0x0000}, // X
    {0x300C,0x1818,0x0C30,0x0660,0x03C0,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0180,0x0000,0x0000,0x0000}, // Y
    {0x7FFF,0x7FFF,0x000E,0x001C,0x0038,0x0070,0x01E0,0x0380,0x0700,0x0E00,0x1C00,0x7FFF,0x7FFF,0x0000,0x0000,0x0000}  // Z
};

// Função para desenhar um caractere
void drawChar(int x, int y, char c) {
    if (c < 0 || c > 127) return;
    const uint16_t* f = font16x16_basic[(uint8_t)c];
    for(int r=0; r<16; r++) {
        uint16_t row = f[r];
        for(int b=0; b<16; b++) {
            if (row & (0x8000 >> b)) display.dot(x + b, y + r, 255);
        }
    }
}

// Função para escrever uma frase
void printString(int x, int y, const char* str) {
    while(*str) {
        drawChar(x, y, *str++);
        x += 12; // Espaçamento entre letras
    }
}

void setup() {
    Serial.begin(115200);
    display.init(CompMode::MODEPAL288P, VIDEO_PIN);
    display.clear();
    
    analogReadResolution(12);
    analogSetPinAttenuation(ADC_PIN, ADC_11db);

    // Pré-cálculo Goertzel
    for (int i = 0; i < 2; i++) {
        float normalizedFreq = targetFreqs[i] / (float)SAMPLE_RATE;
        coeffs[i] = 2.0 * cos(2.0 * PI * normalizedFreq);
    }

    // --- DESENHO DA INTERFACE ESTÁTICA ---
    // Título Centralizado (Largura da tela ~320px, texto ~250px)
    printString(40, 30, "DETECTOR MARK SPACE RTTY"); // Linha e coluna do título

    // Legendas abaixo das barras
    printString(50, 210, "MARK");
    printString(150, 210, "SPACE");

    // Configuração Watchdog
    esp_task_wdt_config_t wdt_config = { .timeout_ms = 5000, .idle_core_mask = 0, .trigger_panic = true };
    esp_task_wdt_init(&wdt_config);
    esp_task_wdt_add(NULL);
}

void loop() {
    int sampleInterval = 1000000 / SAMPLE_RATE;
    for (int i = 0; i < BLOCK_SIZE; i++) {
        unsigned long start = micros();
        samples[i] = analogRead(ADC_PIN) - 2048;
        while (micros() - start < sampleInterval);
    }

    float mag[2];
    for (int f = 0; f < 2; f++) {
        q1[f] = 0; q2[f] = 0;
        for (int i = 0; i < BLOCK_SIZE; i++) {
            float q0 = coeffs[f] * q1[f] - q2[f] + (float)samples[i];
            q2[f] = q1[f]; q1[f] = q0;
        }
        mag[f] = (q1[f]*q1[f] + q2[f]*q2[f] - q1[f]*q2[f]*coeffs[f]) / (BLOCK_SIZE*BLOCK_SIZE);
    }

    // --- ATUALIZAÇÃO DINÂMICA DAS BARRAS ---
    int hMark = map(constrain((int)mag[0], 0, 5000), 0, 5000, 0, 100);
    int hSpace = map(constrain((int)mag[1], 0, 5000), 0, 5000, 0, 100);

    // Barra MARK
    display.fillRect(55, 100, 30, 100, 40);             // Fundo cinza
    display.fillRect(55, 200 - hMark, 30, hMark, 255);  // Barra branca

    // Barra SPACE
    display.fillRect(155, 100, 30, 100, 40);            // Fundo cinza
    display.fillRect(155, 200 - hSpace, 30, hSpace, 255); // Barra branca

    esp_task_wdt_reset();
}



Ainda em testes , depois modificarei para a detecção dos caracteres, que já estão inseridos no código !




Comentários

Postagens mais visitadas deste blog

JVC Compulink, COMPU LINK, JVC, Tape Deck, Arduino, DCS Codes

Projetos elaborados, ferrovia