Serial и Кириллица

Добрый день уважаемые знатоки, по воле случая столкнулся с ардуино. Моя задача - Написать скетч для uno, на вход которого поступает строка через rs232 (И через MAX232), после чего она преобразуется (специальная кодировка/команда) и отправляется на промышленный принтер.
Все сделал, но когда на вход поступает кириллица, вылезают непонятные, битые символы, которые не получается преобразовать. Может ли кто помочь с этим? Сразу говорю, с ардуино не сталкивался до этого, так что могу не понимать элементарные вещи, заранее прошу прощения.
Вот Мой код:

#include <Arduino.h>
#include <SoftwareSerial.h>

// Настройка пинов для программного последовательного порта (для связи с принтером)
const int printerRxPin = 8; // Пин для приема данных от принтера
const int printerTxPin = 9; // Пин для передачи данных на принтер

const int printerRxPin2 = 10; // Пин для приема данных от Компьютера
const int printerTxPin2 = 11; // Пин для передачи данных на Компьютер
 
// Создание объекта SoftwareSerial для связи с принтером
SoftwareSerial printerSerial(printerRxPin, printerTxPin);
SoftwareSerial MySerial(printerRxPin2, printerTxPin2);
// Структура для хранения команды принтера
struct PrinterCommand {
  String functionCode;
  String data;
};

// Функция для преобразования символа в шестнадцатеричную строку
String charToHex(char value) {
  char buffer[5];
  sprintf(buffer, "00%02X", value); 
  return String(buffer);
}

String strToHex(const String &text) {
  String result = "";
  for (size_t i = 0; i < text.length(); ++i) {
    result += charToHex(text[i]);
  }
  return result;
}


// Функция для создания структуры команды принтера
PrinterCommand printerCommandStructure(const String &code) {
  PrinterCommand command = {code, ""};
  return command;
}

String printerPrepareCommandForTransfer(PrinterCommand &command) {
  String result = "";

  // Добавляем код функции
  result += command.functionCode;
  result+="00";
  
  // Добавляем длину данных в шестнадцатеричном формате
  int length = command.data.length();

  result += wordToHex(length);
  result += command.data;
  // Вычисляем контрольную сумму
  uint16_t checksum = 0;
  for (size_t i = 0; i < result.length(); ++i) {
    checksum += result[i];
  }
  checksum %= 65536;
  checksum = (checksum ^ 0xFFFF) + 1;
  // Добавляем контрольную сумму
  result += wordToHex(checksum);
  // Добавляем символы начала и конца
  char SOI = 0x7E;
  char EOI = 0x0D;
  result = String(SOI) + result + String(EOI);
  return result;
}

// Функция для преобразования слова в шестнадцатеричную строку
String wordToHex(uint16_t value) {
  char buffer[5];
  sprintf(buffer, "%04X", value);
  return String(buffer);
}

String printerSetDataForPrinting(const String data[], size_t dataSize) { 
  PrinterCommand command = printerCommandStructure("02");
  command.data = "";
  for (size_t index = 0; index < dataSize; ++index) {
    String hexString = strToHex("X" + String(index) + data[index]); 
    command.data += hexString+"001F";
  }
  return printerPrepareCommandForTransfer(command);
}

void setup() {
  // Настройка программного последовательного порта для связи с принтером
  printerSerial.begin(9600);
  Serial.begin(9600);
  MySerial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    // Чтение строки с входа
    String input = Serial.readString();
    input.trim(); // Удаление пробельных символов с начала и конца строки
    Serial.println(input);
    // Создание массива данных из входной строки
    String data[] = {input};  // Ваши данные

    // Отправка данных в функцию для подготовки команды
    String command = printerSetDataForPrinting(data, 1);
    // Вывод команды на отправку
    Serial.print("Результат обработки: ");
    Serial.println(command);

    // Отправка команды на принтер
    printerSerial.println(command);
    Serial.println("Отправлено");
  }
  if (printerSerial.available() > 0) {
    // Чтение строки с входа
    String input = printerSerial.readString();
    Serial.println(input);
  }
   if (MySerial.available() > 0) {
    // Чтение строки с входа
    String input = MySerial.readString();
    input.trim(); // Удаление пробельных символов с начала и конца строки
    Serial.println(input);
    // Создание массива данных из входной строки
    String data[] = {input};  // Ваши данные

    // Отправка данных в функцию для подготовки команды
    String command = printerSetDataForPrinting(data, 1);

    // Вывод команды на отправку
    Serial.print("Final Command: ");
    Serial.println(command);
// Отправка команды на принтер
    printerSerial.println(command);
    Serial.println("Отправлено");
  }
   
}

Кириллица в какой кодировке?

непонятно, что в вашем понимании “кириллица”, это 2 байта на символ или коды больше 0x7F?
или еще что то, пример входных и выходных данных, есть?

Отправлял с мониторсериал и спец программы для обмена данными через ком порт (Hercules). Точно узнать кодировку невозможно

Сообщение на русском языке (к сожалению мои познания тут кончаются)
Просто русские буквы с клавиатуры, так что наверное utf-8

Вот пример

А придётся. “мониторсериал” отправляет в utf-8. Соответственно в этой кодировки их и нужно обрабатывать. Или преобразовывать в требуемую.

Скажите пожалуйста, как я могу это обработать?

Преобразовать utf-8 в кодировку принтера. Не?

я наверное неправильно выразился. До принтера сообщение не доходит. Вся проблема на стороне ардуино, она не может понять русские символы и начинает выдавать битые символы. Если ардуино сможет понять русские буквы - преобразовать в кодировку принтера не будет проблемой.

Ардуино пофигу что принимать. Ей лишь бы принять.

Я кажется понял вас.
Проблема заключается не в том, что ардуина не понимает русские символы, а в моменте когда она их преобразовывает для принтера, то есть когда она начинает работу с символами, которые отличаются чем-то (наверное количеством байт) от привычных.
Я правильно понял?

Нет. Ардуино принимает то что вы ей передаёте. А уже дальше начинается обработка/преобразование, если оно у вас есть.

Не может она ничего понимать. Она принимает байты, а интерпретируете их уже вы - как напишете, так и будет

1 лайк

Хорошо, я понял. Но в чем проблема кириллицы? Почему английские символы и цифры программа преобразует правильно? На что мне обратить внимание.

Это глобальная проблема. Читайте википедию и т.п.

и еще, в мониторе порта точно скорость 9600 настроена?

Да, скорость я точно настроил

Опять же, кому привычных? В UTF8 кириллица занимает два байта. Первый байт - служебный (0xd0 или 0xd1), второй байт - собственно символ. Преобразование второго байта к коду ANSI зависит от первого байта. Примерно так

uint16_t i = 0;
 while (_str[i] != 0)
  {
    uint8_t chr = (int)_str[i];
    // перекодировка кириллицы из UTF8 в Win-1521 при необходимости
    if (chr >= 0xC0)
    {
      switch (chr)
      {
      case 0xD0:
        i++;
        chr = (int)_str[i];
        if (chr == 0x81)
        {
          chr = 0xA8;
        }
        else if (chr >= 0x90 && chr <= 0xBF)
        {
          chr += 0x30;
        }
        break;
      case 0xD1:
        i++;
        chr = (int)_str[i];
        if (chr == 0x91)
        {
          chr = 0xB8;
        }
        else if (chr >= 0x80 && chr <= 0x8F)
        {
          chr += 0x70;
        }
        break;
      }
    }
    i++;
  }
..........

где _str[] - перекодируемая строка

Потому что в UTF8 они занимают один байт