Беспроводной телеграф на nrf24

Логично.
Тем более,что я просто пошутил, и код в целом не смотрел)

Ради интереса, скормил ИИ последний “настоящий морзе” от BABOS :slight_smile:
С задачей поправить ошибки и загнать массив символов в Progmem.
И вот что мне выдали:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"

// ============== НАСТРОЙКИ ==============
const uint64_t pipe = 0xE8E8F0F0E1LL;
const int LCD_ADDR = 0x27;
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

// Пины
const int PIN_BUZZER = 2;
const int PIN_BUTTON_SYMBOL = 3;
const int PIN_BUTTON_MODE = 4;
const int PIN_LED_TRANSMIT = 5;
const int PIN_LED_RECEIVE = 6;
const int PIN_CE = 9;
const int PIN_CSN = 10;

// Константы времени
const unsigned long DASH_MIN_TIME = 150;
const unsigned long SYMBOL_TIMEOUT = 600;
const unsigned long SCREEN_TIMEOUT = 30000;
const unsigned long DEBOUNCE_DELAY = 50;

// ============== ТАБЛИЦА МОРЗЕ (PROGMEM) ==============
// Формат: "СИМВОЛ|КОД"
const char morseRaw[] PROGMEM = 
  "A|.- B|-... C|-.-. D|-.. E|. F|..-. G|--. H|.... I|.. J|.--- K|-.- L|.-.. M|-- "
  "N|-. O|--- P|.--. Q|--.- R|.-. S|... T|- U|..- V|...- W|.-- X|-..- Y|-.-- Z|--.. "
  "1|.---- 2|..--- 3|...-- 4|....- 5|..... 6|-.... 7|--... 8|---.. 9|----. 0|----- "
  ".|.-.-.- ,|--..-- ?|..--.. !|-.-.-- :|---... ;|-.-.-. =|-...- +|.-.-. -|-....- /|-..-. @|.--.-.";

// ============== ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ==============
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);
RF24 radio(PIN_CE, PIN_CSN);

bool transmitter = true;
bool screen = true;
bool buttonWasUp = true;
unsigned long buttonPressTime = 0;
unsigned long lastActivityTime = 0;
unsigned long lastSymbolTime = 0;

String currentMorse = "";
byte cursorX = 0, cursorY = 0;
byte idx;

// ============== ФУНКЦИИ ПОИСКА ==============

// Ищет символ по коду Морзе во флеш-памяти
char findCharByMorse(String code) {
  char buffer[60];
  strcpy_P(buffer, morseRaw);
  char* token = strtok(buffer, " ");
  while (token != NULL) {
    String t = String(token);
    int sep = t.indexOf('|');
    if (t.substring(sep + 1) == code) return t[0];
    token = strtok(NULL, " ");
  }
  return '?';
}

// Ищет индекс (для радио) или символ для дисплея
String getMorseByIndex(int idx) {
  char buffer[60];
  strcpy_P(buffer, morseRaw);
  char* token = strtok(buffer, " ");
  int count = 0;
  while (token != NULL) {
    if (count == idx) return String(token);
    token = strtok(NULL, " ");
    count++;
  }
  return "";
}

// ============== ЛОГИКА ДИСПЛЕЯ ==============
void displayOn() { lcd.display(); lcd.backlight(); screen = true; lastActivityTime = millis(); }
void displayOff() { lcd.noDisplay(); lcd.noBacklight(); screen = false; }

void addCharToDisplay(char c) {
  displayOn();
  lcd.setCursor(cursorX, cursorY);
  lcd.print(c);
  cursorX++;
  if (cursorX >= LCD_COLS) { cursorX = 0; cursorY++; }
  if (cursorY >= LCD_ROWS) { cursorY = 0; cursorX = 0; lcd.clear(); }
}

// ============== РАДИО ==============
void sendMorse(String code) {
  char buffer[60];
  strcpy_P(buffer, morseRaw);
  char* token = strtok(buffer, " ");
  byte idx = 0;
  while (token != NULL) {
    String t = String(token);
    if (t.substring(t.indexOf('|') + 1) == code) {
      radio.stopListening();
      radio.write(&idx, sizeof(idx));
      if (!transmitter) radio.startListening();
      return;
    }
    token = strtok(NULL, " ");
    idx++;
  }
}

// ============== ОСНОВНЫЕ ЦИКЛЫ ==============
void processTX() {
  bool btn = digitalRead(PIN_BUTTON_SYMBOL);
  if (!btn) { // Нажата
    if (buttonWasUp) { buttonPressTime = millis(); buttonWasUp = false; }
    tone(PIN_BUZZER, 600);
    lastSymbolTime = millis();
    displayOn();
  } else { // Отпущена
    if (!buttonWasUp) {
      noTone(PIN_BUZZER);
      unsigned long duration = millis() - buttonPressTime;
      currentMorse += (duration > DASH_MIN_TIME) ? "-" : ".";
      buttonWasUp = true;
    }
  }

  // Если долго не нажимали — парсим символ
  if (currentMorse.length() > 0 && (millis() - lastSymbolTime > SYMBOL_TIMEOUT)) {
    char found = findCharByMorse(currentMorse);
    addCharToDisplay(found);
    sendMorse(currentMorse);
    currentMorse = "";
    blinkLED(PIN_LED_TRANSMIT);
  }
}

void processRX() {
 
  if (radio.available()) {
//    byte idx;
    radio.read(&idx, sizeof(idx));
    String res = getMorseByIndex(idx);
    if (res != "") addCharToDisplay(res[0]);
    blinkLED(PIN_LED_RECEIVE);
  }
}

void blinkLED(int pin) {
  digitalWrite(pin, HIGH); delay(50); digitalWrite(pin, LOW);
}

void setup() {
  pinMode(PIN_BUZZER, OUTPUT);
  pinMode(PIN_BUTTON_SYMBOL, INPUT_PULLUP);
  pinMode(PIN_BUTTON_MODE, INPUT_PULLUP);
  pinMode(PIN_LED_TRANSMIT, OUTPUT);
  pinMode(PIN_LED_RECEIVE, OUTPUT);

  Serial.begin(9600);
  lcd.init();
  lcd.backlight();
  lcd.print(F("Morse Radio v2"));

  radio.begin();
  radio.setPALevel(RF24_PA_LOW);
  radio.openWritingPipe(pipe);
  radio.openReadingPipe(1, pipe);
  radio.startListening();
  
  delay(1500);
  lcd.clear();
}

void loop() {
  // Переключение режима (TX/RX)
  if (digitalRead(PIN_BUTTON_MODE) == LOW) {
    transmitter = !transmitter;
    lcd.clear();
    lcd.print(transmitter ? F("MODE: TX") : F("MODE: RX"));
    if (transmitter) radio.stopListening(); else radio.startListening();
    delay(1000);
    lcd.clear();
  }

  if (transmitter) processTX(); else processRX();

  // Сон дисплея
  if (screen && (millis() - lastActivityTime > SCREEN_TIMEOUT)) displayOff();
}

Результат компиляции просто поразил.

P.S. Насчёт “рабочести” код не проверял. Сегодня лениво что-то, но вероятно, попробую запустить. Когда-нибудь. :slight_smile:

P.P.S. Только сейчас заметил, что ИИ почему-то выкинул из кода все Serial.print.
Пойду спрошу, что это он так. :slight_smile:

вооо, а говорили не пригодится))) даешь отдельные теоретические ветви решения! главное не запутаться…

Закомментировал все, что касается вывода в порт. Работает частично, но, с ошибками. Постоянно передает ”@” и принимается тоже самое в паузах

Не работает. После вывода на экран приветственной надписи- зависает напрочь. Режимы работы не переключаются.

это в какой версии кода ?)))

char buffer[60];  // 60 байт - ЭТО СЛИШКОМ МАЛО!
strcpy_P(buffer, morseRaw);  // morseRaw намного больше 60 символов

В processRX() переменная idx закомментирована, используется глобальная — это приводит к конфликтам.

если у него будет время может поправит)))

Резиновый?

Да, ладно! Печально!

Это уже я накосячил.. :flushed_face:
Компилятор ругнул " warning: ‘idx’ may be used uninitialized in this function [-Wmaybe-uninitialized]
String res = getMorseByIndex(idx);"
Вот я и повёлся, запулил глобально не подумавши..

Гм.. А вот тут интересно.
Когда я кинул предъяву ИИ, насчёт того, куда он дел Serial.print, он выдал мне новый код, где buffer уже 128 !

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"

// ============== НАСТРОЙКИ ==============
const uint64_t pipe = 0xE8E8F0F0E1LL;
const int LCD_ADDR = 0x27;
const int LCD_COLS = 16;
const int LCD_ROWS = 2;

const int PIN_BUZZER = 2;
const int PIN_BUTTON_SYMBOL = 3;
const int PIN_BUTTON_MODE = 4;
const int PIN_LED_TRANSMIT = 5;
const int PIN_LED_RECEIVE = 6;
const int PIN_CE = 9;
const int PIN_CSN = 10;

const unsigned long DASH_MIN_TIME = 150;
const unsigned long SYMBOL_TIMEOUT = 600;
const unsigned long SCREEN_TIMEOUT = 30000;

// ============== ТАБЛИЦА МОРЗЕ В PROGMEM ==============
const char morseRaw[] PROGMEM = 
  "A|.- B|-... C|-.-. D|-.. E|. F|..-. G|--. H|.... I|.. J|.--- K|-.- L|.-.. M|-- "
  "N|-. O|--- P|.--. Q|--.- R|.-. S|... T|- U|..- V|...- W|.-- X|-..- Y|-.-- Z|--.. "
  "1|.---- 2|..--- 3|...-- 4|....- 5|..... 6|-.... 7|--... 8|---.. 9|----. 0|----- "
  ".|.-.-.- ,|--..-- ?|..--.. !|-.-.-- :|---... ;|-.-.-. =|-...- +|.-.-. -|-....- /|-..-. @|.--.-.";

// ============== ПЕРЕМЕННЫЕ ==============
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS);
RF24 radio(PIN_CE, PIN_CSN);

bool transmitter = true;
bool screen = true;
bool buttonWasUp = true;
unsigned long buttonPressTime = 0;
unsigned long lastActivityTime = 0;
unsigned long lastSymbolTime = 0;
String currentMorse = "";
byte cursorX = 0, cursorY = 0;

// ============== ФУНКЦИИ ПОИСКА ==============

char findCharByMorse(String code) {
  char buffer[128]; // Временный буфер для парсинга части строки
  strcpy_P(buffer, morseRaw);
  char* token = strtok(buffer, " ");
  while (token != NULL) {
    String t = String(token);
    int sep = t.indexOf('|');
    if (t.substring(sep + 1) == code) return t[0];
    token = strtok(NULL, " ");
  }
  return '?';
}

String getMorseByIndex(int idx) {
  char buffer[128];
  strcpy_P(buffer, morseRaw);
  char* token = strtok(buffer, " ");
  int count = 0;
  while (token != NULL) {
    if (count == idx) {
      String t = String(token);
      return t.substring(0, t.indexOf('|'));
    }
    token = strtok(NULL, " ");
    count++;
  }
  return "?";
}

// ============== ЛОГИКА ==============

void addCharToDisplay(char c) {
  displayOn();
  lcd.setCursor(cursorX, cursorY);
  lcd.print(c);
  Serial.print(F("Вывод на LCD: ")); Serial.println(c);
  cursorX++;
  if (cursorX >= LCD_COLS) { cursorX = 0; cursorY++; }
  if (cursorY >= LCD_ROWS) { cursorY = 0; cursorX = 0; lcd.clear(); }
}

void sendMorse(String code) {
  char buffer[128];
  strcpy_P(buffer, morseRaw);
  char* token = strtok(buffer, " ");
  byte idx = 0;
  while (token != NULL) {
    String t = String(token);
    if (t.substring(t.indexOf('|') + 1) == code) {
      radio.stopListening();
      bool ok = radio.write(&idx, sizeof(idx));
      Serial.print(F("Радио -> Отправка индекса ")); Serial.print(idx);
      Serial.println(ok ? F(" [OK]") : F(" [ОШИБКА]"));
      if (!transmitter) radio.startListening();
      return;
    }
    token = strtok(NULL, " ");
    idx++;
  }
}

void displayOn() { lcd.display(); lcd.backlight(); screen = true; lastActivityTime = millis(); }
void displayOff() { lcd.noDisplay(); lcd.noBacklight(); screen = false; Serial.println(F("LCD: Спящий режим")); }

void setup() {
  Serial.begin(9600);
  while (!Serial); // Ждем открытия порта
  Serial.println(F("--- Morse Radio Nano v3 (PROGMEM) ---"));

  pinMode(PIN_BUZZER, OUTPUT);
  pinMode(PIN_BUTTON_SYMBOL, INPUT_PULLUP);
  pinMode(PIN_BUTTON_MODE, INPUT_PULLUP);
  pinMode(PIN_LED_TRANSMIT, OUTPUT);
  pinMode(PIN_LED_RECEIVE, OUTPUT);

  lcd.init();
  lcd.backlight();
  lcd.print(F("Morse Radio v3"));

  if (!radio.begin()) {
    Serial.println(F("Ошибка: nRF24L01 не найден!"));
    lcd.setCursor(0,1); lcd.print(F("RF ERROR!"));
  }
  
  radio.setPALevel(RF24_PA_LOW);
  radio.openWritingPipe(pipe);
  radio.openReadingPipe(1, pipe);
  radio.startListening();

  Serial.println(F("Система готова. Режим: RX (Прием)"));
  transmitter = false; // Начинаем с приема для безопасности
  
  delay(1500);
  lcd.clear();
}

void loop() {
  // Переключение режима
  if (digitalRead(PIN_BUTTON_MODE) == LOW) {
    transmitter = !transmitter;
    lcd.clear();
    Serial.print(F("Смена режима: "));
    if (transmitter) {
      radio.stopListening();
      lcd.print(F("MODE: TX"));
      Serial.println(F("ПЕРЕДАЧА"));
    } else {
      radio.startListening();
      lcd.print(F("MODE: RX"));
      Serial.println(F("ПРИЕМ"));
    }
    delay(500);
    lcd.clear();
    lastActivityTime = millis();
  }

  // Логика передачи
  if (transmitter) {
    bool btn = digitalRead(PIN_BUTTON_SYMBOL);
    if (!btn) {
      if (buttonWasUp) { buttonPressTime = millis(); buttonWasUp = false; }
      tone(PIN_BUZZER, 650);
      lastSymbolTime = millis();
    } else {
      if (!buttonWasUp) {
        noTone(PIN_BUZZER);
        unsigned long duration = millis() - buttonPressTime;
        currentMorse += (duration > DASH_MIN_TIME) ? "-" : ".";
        buttonWasUp = true;
        Serial.print(F("Сигнал: ")); Serial.println(currentMorse);
      }
    }

    if (currentMorse.length() > 0 && (millis() - lastSymbolTime > SYMBOL_TIMEOUT)) {
      char found = findCharByMorse(currentMorse);
      Serial.print(F("Символ распознан: ")); Serial.println(found);
      addCharToDisplay(found);
      sendMorse(currentMorse);
      currentMorse = "";
      digitalWrite(PIN_LED_TRANSMIT, HIGH); delay(50); digitalWrite(PIN_LED_TRANSMIT, LOW);
    }
  } 
  // Логика приема
  else {
    if (radio.available()) {
      byte idx = 0;
      radio.read(&idx, sizeof(idx));
      String res = getMorseByIndex(idx);
      Serial.print(F("Принято радио! Индекс: ")); Serial.print(idx);
      Serial.print(F(" Символ: ")); Serial.println(res);
      if (res != "?") addCharToDisplay(res[0]);
      digitalWrite(PIN_LED_RECEIVE, HIGH); delay(50); digitalWrite(PIN_LED_RECEIVE, LOW);
    }
  }

  if (screen && (millis() - lastActivityTime > SCREEN_TIMEOUT)) displayOff();
}

P.S. Вообщем, развлекайтесь дальше без меня, господа!
Мне надо сегодня к паяльнику. И так кучу времени потратил на развлекуху с ИИ.. :slight_smile:

Эти ваши вайбкодинги до добра не доведут…

Впаивать или выпаивать? Или и то и другое?))

а если еще ругнуться вернет все символы ?)))
а то тут кажется их меньше…