Не могу вывести русские символы на OLED SSD1306 в char

maxim_ov

✩✩✩✩✩✩✩
4 Июл 2025
5
0
Всем добрый день!
Нашел для ардуино и олед-дисплея программку для тренажера кода Морзе, но в ней только английские буквы и цифры. Я хотел откорректировать на русские. загрузил все библиотеки, русификаторы utf8rus,
если выводить символы через:
display.println(utf8rus("КОД МОРЗЕ"));
все без проблем, но вот столкнулся с загвоздкой - при наборе точек и тире вывод на экран происходит через char? а вот как его пропустить через utf8rus я не могу понять.
C++:
void drawChar(char c) {
  display.println(c);
  display.display();
}
char decodeMsg() {
  char c = '?';
  if( isDot(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && buttonPressTimes[2] == 0 )
    c = 'A';
    else if( isDot(buttonPressTimes[0]) && buttonPressTimes[1] == 0 )
    c = 'Е';
если писать в строках 8 и 10 в кавычках русские символы, то на дисплей выводится ерунда, а если сделать в строке 2
display.println(utf8rus(c)); выдает ошибку

я пока новичок в этом деле, может что в синтаксисе не понимаю, подскажите пожалуйста!


PS на всякий случай полный код программы:
C++:
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/FreeSerif9pt7b.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
#define BUZZER_PIN 2
#define LED_PIN 7
#define CODE_BUTTON 3
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

bool codeButtonArmed;
bool codeButtonPressed;
unsigned long codeTime;
unsigned long startTime;
unsigned long lastButtonPressTime;
bool letterDecoded;
bool newWord;

// Time range of a dot in milliseconds
const unsigned int dotTimeMillisMin = 40;
const unsigned int dotTimeMillisMax = 180;

// Array to store the times of the code button presses
unsigned long buttonPressTimes[5];
int bptIndex;

int row;
int col;

void setup() {
  pinMode(CODE_BUTTON, INPUT);
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);
  Serial.begin(9600);
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println("SSD1306 allocation failed");
    for(;;); // Don't proceed, loop forever
  }
  display.clearDisplay();
  display.setTextSize(2);      // Normal 1:1 pixel scale
  display.setTextColor(SSD1306_WHITE); // Draw white text
  display.setCursor(0, 0);     // Start at top-left corner
  display.cp437(true);         // Use full 256 char 'Code Page 437' font
  /*display.write("Morse Code Generator");*/
  display.println(utf8rus("КОД МОРЗЕ"));
  display.drawLine(0, 16, display.width(), 16, SSD1306_WHITE);
  display.setTextSize(1); 
  display.setCursor(0, 21);
  display.display();
  delay(200);
}

void loop() {
  scanButtons();
  if( millis() - lastButtonPressTime > 1600 && newWord == true ) {
    Serial.println("New word");
    display.write(' ');
    display.display();
    newWord = false;
  }else if( millis() - lastButtonPressTime > 600 && letterDecoded == false) {
    decodeButtonPresses();
    letterDecoded = true;
  }
}

void codeButtonDown() {
  tone(BUZZER_PIN, 440, 10);
  digitalWrite(LED_PIN, HIGH);
  codeTime = millis() - startTime;
}
void codeButtonReleased() {
  digitalWrite(LED_PIN, LOW);

  // Most button bounces take less than 25 millis. If the code time
  // is greater than 25 millis then it was probably a legit button press
  if( codeTime > 25 ) {
    
    Serial.print("Code time: ");
    Serial.print(codeTime);
    Serial.println(" milliseconds");

    // Save codeTime
    buttonPressTimes[bptIndex] = codeTime;
    bptIndex++;
    if( bptIndex == 5 ) {
      
      for(int i=0; i<5; i++) {
        buttonPressTimes[bptIndex] = 0;
      }
    }
  }
}

void scanButtons() {
  if( ! codeButtonArmed && digitalRead(CODE_BUTTON) == HIGH ) {
    codeButtonArmed = true;
    // start timer
    startTime = millis();
    lastButtonPressTime = startTime;
    codeTime = 0;
    letterDecoded = false;
    newWord = true;
  }
  if( digitalRead(CODE_BUTTON) == HIGH ) {
    codeButtonPressed = true;
    codeButtonDown();
  }
  if( codeButtonPressed && digitalRead(CODE_BUTTON) == LOW ) {
    codeButtonPressed = false;
    codeButtonReleased();
    codeButtonArmed = false;
  }
}

void decodeButtonPresses() {
  Serial.print("DECODE LETTER: ");
  for(int i=0; i<bptIndex; i++) {
    if( isDot(buttonPressTimes[i]) )
      Serial.print(" DOT ");
    else if( isDash(buttonPressTimes[i]) )
      Serial.print(" DASH");
  }
  Serial.print("   ");
  char c = decodeMsg();
  Serial.print(c);
  Serial.println();
  display.write(c);
  display.display();
  bptIndex = 0;
  for(int i=0; i<5; i++) {
    buttonPressTimes[i] = 0;
  }
}
bool isDot(unsigned long t) {
  if( t >= dotTimeMillisMin && t <= dotTimeMillisMax )
    return true;
  return false;
}
bool isDash(unsigned long t) {
  if( t > dotTimeMillisMax )
    return true;
  return false;
}

void drawChar(char c) {
  display.println(c);
  display.display();
}
char decodeMsg() {
  char c = '?';
  if( isDot(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && buttonPressTimes[2] == 0 )
    c = 'A';
  else if( isDash(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'B';
  else if( isDash(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'C';
  else if( isDash(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && buttonPressTimes[3]== 0 )
    c = 'D';
  else if( isDot(buttonPressTimes[0]) && buttonPressTimes[1] == 0 )
    c = 'А';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'F';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && buttonPressTimes[3]== 0 )
    c = 'G';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'H';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && buttonPressTimes[2]== 0 )
    c = 'I';
  else if( isDot(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'J';
  else if( isDash(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && buttonPressTimes[3]== 0 )
    c = 'K';
  else if( isDot(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'L';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && buttonPressTimes[2]== 0 )
    c = 'M';
  else if( isDash(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && buttonPressTimes[2]== 0 )
    c = 'N';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && buttonPressTimes[3]== 0 )
    c = 'O';
  else if( isDot(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'P';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'Q';
  else if( isDot(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && buttonPressTimes[3]== 0 )
    c = 'R';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && buttonPressTimes[3]== 0 )
    c = 'S';
  else if( isDash(buttonPressTimes[0]) && buttonPressTimes[1]== 0 )
    c = 'T';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && buttonPressTimes[3]== 0 )
    c = 'U';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'V';
  else if( isDot(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && buttonPressTimes[3]== 0 )
    c = 'W';
  else if( isDash(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'X';
  else if( isDash(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'Y';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && buttonPressTimes[4]== 0 )
    c = 'Z';
  else if( isDot(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && isDash(buttonPressTimes[4]))
    c = '1';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && isDash(buttonPressTimes[4]))
    c = '2';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && isDash(buttonPressTimes[4]))
    c = '3';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && isDash(buttonPressTimes[4]))
    c = '4';
  else if( isDot(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && isDot(buttonPressTimes[4]))
    c = '5';
  else if( isDash(buttonPressTimes[0]) && isDot(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && isDot(buttonPressTimes[4]))
    c = '6';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDot(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && isDot(buttonPressTimes[4]))
    c = '7';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDot(buttonPressTimes[3]) && isDot(buttonPressTimes[4]))
    c = '8';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && isDot(buttonPressTimes[4]))
    c = '9';
  else if( isDash(buttonPressTimes[0]) && isDash(buttonPressTimes[1]) && isDash(buttonPressTimes[2]) && isDash(buttonPressTimes[3]) && isDash(buttonPressTimes[4]))
    c = '0';
  return c;
}


/* Функция перекодировки русских букв из UTF-8 в Win-1251 */

String utf8rus(String source)
{
  int i,k;
  String target;
  unsigned char n;
  char m[2] = { '0', '\0' };
  k = source.length(); i = 0;
  while (i < k) {
    n = source[i]; i++;
    if (n >= 0xC0) {
      switch (n) {
        case 0xD0: {
          n = source[i]; i++;
          if (n == 0x81) { n = 0xA8; break; }
          if (n >= 0x90 && n <= 0xBF) n = n + 0x30;
          break;
        }
        case 0xD1: {
          n = source[i]; i++;
          if (n == 0x91) { n = 0xB8; break; }
          if (n >= 0x80 && n <= 0x8F) n = n + 0x70;
          break;
        }
      }
    }
    m[0] = n; target = target + String(m);
  }
return target;
}
 

maxim_ov

✩✩✩✩✩✩✩
4 Июл 2025
5
0
@asaitov,
не получилось, на дисплее хаотичные пиксели в букве, такое чувство что он не видит utf8rus, как просто с println(c)
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
637
190
@maxim_ov,
Ваша задача наверное учебная. И правильно будет изучить то, для чего она предназначена. И решить ее тем способом, который соответствует теме, т.к. решить ее можно разными способами.
Решать надо по шагам.
Прочитать про кодировки, чем отличается UTF-8 от Win-1251
Понять, что русский символ в UTF-8 не поместится в char c
Решить, что делать дальше (вариантов много, приведу два для примера)
- писать русские символы сразу в кодировке Win-1251 char c = '?'; c = 222; //Буква Ю
(в этом случае встроенный монитор com порта не будет их корректно отображать, но можно использовать другой терминал)
- писать русские символы в кодировке редактора (В данном случае UTF-8)
Тогда КАК ВАРИАНТ использовать String вместо char. String c = "?"; c = "Ю"; //Буква Ю
и utf8rus(c) при выводе на дисплей.
 
Изменено:
  • Лойс +1
Реакции: maxim_ov

maxim_ov

✩✩✩✩✩✩✩
4 Июл 2025
5
0
@Bruzzer,
не учебная, сын попросил сделать, в интернете наткнулся на программу, собрал, работает. но там только с английскими символами... остальное - я описал выше.
при попытке заменить на String c='?' выдает ошибку - conversion from 'char' to 'String' is ambiguous. Наверное где-то еще надо менять char на String?
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
637
190
@maxim_ov,
Я привел не готовое решение, а путь решения, и менять надо не только это (да и поменяли вы не так, как у меня написано). Если вы не программируете на Ардуино и не планируете, то вам мой совет не подходит.
 

tangous

✩✩✩✩✩✩✩
3 Июл 2025
5
1
Одним словом, убедитесь, что файл в котором Вы пишите код и русские буквы - в кодировке UTF-8.
Это легко сделать с помощью Notepad++.
В нем же легко поменять кодировку.

1751852434773.png
 

maxim_ov

✩✩✩✩✩✩✩
4 Июл 2025
5
0
@tangous,
да, UTF-8 в блокноте показывает
Безымянный.png

причем в мониторе порта показывает нормально, а на oled дисплее не пойми что в символе...
а команда display.println(utf8rus("КОД МОРЗЕ")); на дисплее высветилась нормально,

в предыдущих ответах прозвучало что char не может вывести два байта (русская буква), как и чем его можно заменить?
 
Изменено:

tangous

✩✩✩✩✩✩✩
3 Июл 2025
5
1
  • Лойс +1
Реакции: maxim_ov

Bruzzer

★★★✩✩✩✩
23 Май 2020
637
190
@maxim_ov,
Еще один вариант решения вашей задачи, использование Arduino IDE первой версии (например 1.8.19) и внешнего редактора в котором выбрать кодировку Win-1251. Тогда с русскими буквами в программе можно работать так же как и с латинскими. Этот способ чаще использовался раньше, когда памяти было мало, готовых библиотек для UTF8 не было. Как уже писал, в случае использования Win-1251, монитор COM порта из Arduino IDE будет отображать русские символы неправильно, но можно использовать отдельную программу терминала поддерживающую Win-1251.
Как использовать внешний редактор можно найти в инете. Если коротко, то поставить галочку "Использовать внешний редактор" в настройках Arduino IDE версии 1.8.19. После этого скетч становится недоступным для редактирования в Arduino IDE, скетч надо редактировать и сохранять во внешнем редакторе.
1751953774711.png
Можно использовать портативную установку Arduino IDE версии 1.8.19, их может быть несколько, и они никак не влияют на другие установленные версии. https://www.arduino.cc/en/Guide/PortableIDE/

Повторюсь использование внешнего редактора с кодировкой Win-1251 , это один из вариантов, со своими плюсами и минусами.
 
  • Лойс +1
Реакции: maxim_ov

maxim_ov

✩✩✩✩✩✩✩
4 Июл 2025
5
0
@tangous,
да, в библиотеке менял шрифт

@Bruzzer,
Спасибо за совет выше. Домучился написать русские символы сразу в кодировке Win-1251
char c = '\317'; в итоге в мониторе порта "⸮" а на oledдисплее буква "П".
 

asaitov

★✩✩✩✩✩✩
16 Янв 2024
34
13
На всякий случай. В Arduino IDE 2.x тоже можно сменить кодировку редактора. Нужно нажать F1 и выбрать пункт > Editor: Change File Encoding, тогда можно будет переоткрыть или сохранить файл в нужной кодировке.
 
  • Лойс +1
Реакции: maxim_ov и Bruzzer