Отправка буквы за буквой на ЖК-дисплей, однако дисплей знает полную строку?

Я думаю, что это мой последний пост об этом конкретном проекте, но сейчас у меня действительно странный поворот

Мне удалось создать нужную мне веб-страницу и передать строку дальше. Теперь, когда загорается светодиод, он отправляет соответствующую букву на ЖК-дисплей.

Проблема сейчас в том, что каким-то образом, когда я отправляю длинную строку, ЖК-дисплей каким-то образом "знает" и отображает переполнение во второй строке, что для меня не имеет никакого смысла. Все, о чем я могу думать, это то, что Ethernet Shield использует те же соединения, что и мой I2C, который подключается к ЖК-дисплею.

Как бы то ни было, вот мой код:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <Ethernet.h>
#define latchPin 5
#define clockPin 7
#define dataPin 6

LiquidCrystal_I2C lcd(0x27,16,2);
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
EthernetServer server(80);
String text; 
char readHttp[100];
int i;
boolean temtexto;
char lcdtext[16];
void setup() {
  Serial.begin(9600);
  for (int i = 2; i < 7; i++) {
    if (i != 4) {
      pinMode(i, OUTPUT);
    }
   }
  pinMode(8,OUTPUT);
  pinMode(9,OUTPUT);
  lcd.init();
  lcd.setBacklight(HIGH);
  lcd.setCursor(0,0);
  lcd.print("INICIANDO");
  Serial.println("Iniciando");
  if (Ethernet.begin(mac) == 0) {
    lcd.clear();
    lcd.setCursor(1,0);
    lcd.print("FALHA AO PEGAR");
    Serial.println("FALHA");
    lcd.setCursor(7,1);
    lcd.print("IP");
    delay(5000);
    return;
  }

  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("PRONTO");
  lcd.setCursor(0,1);
  lcd.print(Ethernet.localIP());
  lcd.setCursor(0,0);
  Serial.println(Ethernet.localIP());
  server.begin();
  brilho(255);
  inicializationEffect();
}

void loop(){
  EthernetClient client = server.available();
  if (client) {
    while (client.connected()) {
      if (client.available()) {
        lcd.setBacklight(LOW);
        lcd.clear();
        char c = client.read();
        if (c == ';') { temtexto = false; readHttp[i] = '\n';}
        if (temtexto) {
          if(i < 100) {
            readHttp[i] = toupper(c);
            i++;
          } 
        }
        if (c == '?') { temtexto = true; }
        if (c == '\n') {
          client.println("HTTP/1.1 200 OK"); // Envia nova pagina
          client.println("Content-Type: text/html");
          client.println("Connection: close"); 
          client.println();
          client.println("<HTML>");
          client.println("<HEAD>");
          client.println("<TITLE>Parede Stranger Things</TITLE>");
          client.println("</HEAD>");
          client.println("<BODY>");
          client.println("<H1>Qual texto deseja enviar?</H1>");
          client.println("<input type=text id=texto style=width:295px  maxlength=50><br>");
          client.println("<input type=submit value='Enviar' style=width:150px;height:45px onClick=location.href='/?'+document.getElementById('texto').value+';'>");
          client.println("</BODY>");
          client.println("</HTML>");
          client.println("<string>");

          
          client.println("</string>");          
          delay(1);
          client.stop();
          char *leader = readHttp;
          char *follower = leader;
          
          // While we're not at the end of the string (current character not NULL)
          while (*leader) {
              // Check to see if the current character is a %
              if (*leader == '%') {
          
                  // Grab the next two characters and move leader forwards
                  leader++;
                  char high = *leader;
                  leader++;
                  char low = *leader;
          
                  // Convert ASCII 0-9A-F to a value 0-15
                  if (high > 0x39) high -= 7;
                  high &= 0x0f;
          
                  // Same again for the low byte:
                  if (low > 0x39) low -= 7;
                  low &= 0x0f;
          
                  // Combine the two into a single byte and store in follower:
                  *follower = (high << 4) | low;
              } else {
                  // All other characters copy verbatim
                  *follower = *leader;
              }
          
              // Move both pointers to the next character:
              leader++;
              follower++;
          }
          // Terminate the new string with a NULL character to trim it off
          *follower = 0;
          lcd.clear();
          executeEffect(readHttp);
          memset(readHttp, 0, sizeof readHttp);
          i = 0;
        }
      }
    }
  }
}




void lineActivation(int line, int keepalive) {
  if (keepalive == 0) {
    for (int i = 2; i <= 4; i++) {
      if (i == 4) { i = 8; } //porta 4 usada pelo SD do Ethernet
      digitalWrite(i, LOW);
    }
  }
  if (line >= 1 && line <=3){
    if (line == 3) { line = 7; } //porta 4 usada pelo SD do Ethernet
    digitalWrite(line+1, HIGH); //coloca a linha 1 em estado lógico alto
  }
}

void activateLetter(char letter, int on, int off) {
  int lt;
  lt = (((int)letter - 64) % 9);
  if (((int)letter - 64) > 8) {
    lt = lt + 1;
  }
  if (on) {
    callCol(1<<lt-1);
    delay(on);
  }
  if (off) {
    callCol(0);
    delay(off);
  }
}

void callCol(int col){
  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, highByte(col));
  shiftOut(dataPin, clockPin, MSBFIRST, lowByte(col));
  digitalWrite(latchPin, HIGH);
}

void executeEffect(char text[]) {
  if (strlen(text) > 0) {
    brilho(255);
    lcd.setBacklight(HIGH);
    memset(lcdtext, ' ', sizeof lcdtext);  
  }
  for (int i = 0; i < strlen(text); i++) {
    if (text[i] == ' ') {
      lcdScreen(' ');
      delay(1000);
    } else if (((int)text[i] < -58 && (int)text[i] > -65) || ((int)text[i] < -28 && (int)text[i] > -33)) {
      text[i] = 'A';
    } else if (((int)text[i] < -47 && (int)text[i] > -41) || ((int)text[i] < -9 && (int)text[i] > -14)) {
      text[i] = 'O';
    } else if (((int)text[i] < -52 && (int)text[i] > -57) || ((int)text[i] < -20 && (int)text[i] > -25)) {
      text[i] = 'E';
    } else if (((int)text[i] < -49 && (int)text[i] > -53) || ((int)text[i] < -16 && (int)text[i] > -21)) {
      text[i] = 'I';
    } else if (((int)text[i] < -35 && (int)text[i] > -40) || ((int)text[i] < -3 && (int)text[i] > -8)) {
      text[i] = 'U';
    } else if (((int)text[i] == -25 || (int)text[i] == -57)) {
      text[i] = 'C';
    }
    if ((int)text[i] < 65 or (int)text[i] > 90) {
    } else {
      lineActivation(int(((int)text[i] - 64) / 9) + 1, 0);
      //Serial.println(text[i]);
      lcdScreen(text[i]);
      activateLetter(text[i], 1000, 200);
    }
  }
  for (int i = 0; i < 16; i++){
    lcdScreen(' ');
    delay(500);
  }
  lcd.setBacklight(LOW);
  lcd.clear();
}

void lcdScreen(char ltc) {
  Serial.println(ltc);
  memcpy(lcdtext, &lcdtext[1], sizeof(lcdtext) - sizeof(char));
  lcdtext[15] =  ltc;
  lcd.setCursor(0,0);
  lcd.print(lcdtext);
}

void inicializationEffect() {
  Serial.println("LineByLine");
  for (int i = 1; i < 27; i++) {
    lineActivation(int(i / 9) + 1, 0);//ativa a linha
    activateLetter(char(64 + i), 100, 10); //ativa a letra
  }
}

void waveEffect() {
  Serial.println("Wave");
  for (int i = 1; i < 4; i++) {
    lineActivation(i, 1);//ativa a linha
  }
  for (int i = 1; i < 10; i++) {
    activateLetter(char(72 + i), 100, 0);
  }

  for (int i = 1; i < 10; i++) {
    activateLetter(char(72 + i), 0, 100);
  }
  lineActivation(0, 0);
}

void blink() {
  Serial.println("Blink");
  callCol(511);
  lineActivation(1,1);
  lineActivation(2,1);
  lineActivation(3,1);
  for (int i = 0; i < 256; i++){
    brilho(i);
    delay(1);
  }
  delay(100);
  for (int i = 255; i >= 0; i--){
    brilho(i);
    delay(1);
  }
}

void brilho(byte brt) {
  analogWrite(9, 255-brt);
}

Я использую сетевой экран W5100, адаптер I2C для ЖК-дисплея и ЖК-дисплей 16x2. I2C подключается к выводам A4 (SDA) и A5 (SCL).

Вот изображение того, как отображается ЖК-дисплей, когда я отправляю "Это действительно длинная строка":

В первой строке текст прокручивается по одному символу за раз, как и было задумано. Однако во второй строке происходит переполнение текста, который, насколько я могу судить, я вообще никогда не отправлял на ЖК-дисплей. Даже если я попытаюсь выполнить функцию lcd.clear() после каждого отправленного символа, она все равно сохранится с концом строки во второй строке.

, 👍1


2 ответа


2

Я думаю, то, что вы видите, является результатом:

lcd.print(lcdtext);

и тот факт, что lcdtext не завершается нулем. print() ожидает строку, то есть некоторую последовательность из нуля или более символов, отличных от '\0', за которыми следует символ '\0'. Не завершив его с помощью null самостоятельно, на практике он продолжит чтение памяти после вашего расположения lcdtext в памяти, пока не найдет '\0'. Где-то там он, вероятно, находит дополнительные символы, которые вы видите.

Вы могли бы сделать так, чтобы в конце всегда было '\0', увеличив lcdtext на один символ больше, если это необходимо для этого. Или вы могли бы:

lcd.write(lcdtext, sizeof lcdtext);

В этой форме с длиной / размером в качестве второго аргумента write не ожидает строку, просто произвольный фрагмент символов, независимо от того, содержат ли они '\0' или нет, и знает, что нужно остановиться на любом количестве символов, которое было указано.

В качестве исключения следует использовать memmove, а не memcpy, когда входные и выходные назначения перекрываются. Что происходит с memcpy, когда они перекрываются, не определено:

memmove(lcdtext, &lcdtext[1], sizeof(lcdtext) - 1);

memmove - это внешне то же самое, только разработанное для этого сценария. Разница между ними заключается в том, какие оптимизации включаются благодаря знанию того, что источник и место назначения могут перекрываться, а могут и не перекрываться. Возможно, это не является источником проблемы на вашей текущей платформе, в зависимости от того, как был написан memcpy, но это может произойти позже или при смене платформы.

,

Я заменил memcpy на memmove, и он работал так же, как и раньше. Функция lcd.write не включена<LiquidCrystal_I2C.h>, это библиотека, которую я использую для I2C для связи со светодиодом. Добавление '\0' в конец просто удалило 4 строки в конце. КОЛЬЦО все еще появлялось, @Feeds

@Feeds, я не удивлен, что memmove () не имеет никакого значения для AVR. Я немного удивлен, что вы не можете использовать эту форму write(), поскольку она является частью класса Print, который предоставляет print () и println() среди прочих. У вас есть ссылка на точную версию библиотеки LiquidCrystal_I2C, которую вы используете?, @timemage

Теперь, когда я думаю об этом, проблема может заключаться в том, что lcdtext является не строкой, а массивом символов. Библиотека, которую я использую, - это библиотека sketch https://github.com/johnrickman/LiquidCrystal_I2C , версия 1.1.2, @Feeds

В https://github.com/johnrickman/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.h#L55 вы можете увидеть, где печать унаследована. В https://github.com/johnrickman/LiquidCrystal_I2C/blob/master/LiquidCrystal_I2C.cpp#L10 реализована функция записи одного символа(). https://github.com/arduino/ArduinoCore-avr/blob/1.8.3/cores/arduino/Print.cpp#L34 где реализация по умолчанию предназначена для упомянутой двухпараметрической версии write(), используется односимвольная функция write()., @timemage

Другими словами, это должно работать нормально. Если это камень преткновения, уделите этому некоторое время, и если вы все еще застряли, возможно, начните новый вопрос с сокращенного примера кода, спрашивающего об этом одном аспекте., @timemage

Сейчас я далеко от своего arduino, но я проведу тестирование, как только вернусь домой, чтобы сообщить о своих выводах. Я все еще в недоумении от того, как работает ЖК-дисплей, @Feeds


0

Хм, я вроде как решил эту проблему, но я все еще не понимаю, как lcdtext получал полную строку.

Я заменил функцию ЖК-экрана следующим образом:

void lcdScreen(char ltc) {
  Serial.println(ltc);
  Serial.print(lcdtext);
  memmove(lcdtext, &lcdtext[1], sizeof(lcdtext) - 1);
  lcdtext[15] =  ltc;
  lcd.setCursor(0,0);
  for (int i = 0; i < 16; i++){
    lcd.print(lcdtext[i]);
  }
}

Это работает, но я все еще не понимаю, почему у меня был полный текст, когда я просто вызываю эту функцию с одним символом каждый раз

,