1

I think this is my last post about this specific project, but I got a really weird curveball right now

I managed to do the webpage I wanted, and pass the string along. Now, when a LED lights up, it sends the corresponding letter to the LCD.

The problem now is that somehow, when I send a long string, the LCD somehow "knows" and display the overflow on the second row, which makes no sense to me. All I can think of is that the Ethernet Shield is using the same connections as my I2C that connects to the LCD.

That being said, here is my code:

#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);
}

I am using a W5100 Ethernet Shield, a I2C to LCD adapter and a 16x2 LCD. The I2C is connected to the pins A4(SDA) and A5(SCL).

Here is a picture of how the LCD appears when I send "This is a really long string":

enter image description here

On the first line the text scrolls, one character at a time, as intended. However, on the second line there is an overflow of the text that, as far as I can tell, I never sent to the LCD in the first place. Even if I try to lcd.clear() after every character sent, it still persists with the end of the string on the second line.

2

I think what you're seeing is the result of:

lcd.print(lcdtext);

and the fact that lcdtext is not null-terminated. print() is expecting a string, that is some sequence of zero or more non-'\0' characters followed by a '\0' character. Not having null-terminated it yourself, in practice it will continue reading memory after your the location of lcdtext in memory until it finds a '\0'. Somewhere in there it is probably finding the extra character(s) that you're seeing.

You could make a point of having '\0' at the end of it always, making lcdtext one character larger if necessary in order to do that. Or you could:

lcd.write(lcdtext, sizeof lcdtext);

In this form with the length/size as a second argument, write doesn't expect a string, just an arbitrary chunk of characters whether they contain '\0' or not and knows to stop at whatever count of characters was specified.

As an aside memmove should be used rather than memcpy when the input and output destinations overlap. What happens with memcpy when they do overlap is undefined:

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

memmove is outwardly the same thing, only designed for this scenario. The difference between them is a matter of which optimizations are enabled by the knowledge that the source and destination may or may not overlap. It may not be a source of a problem on your current platform, depending on how memcpy was written, but it may later or when you switch platforms.

  • I replaced memcpy with memmove and it worked the same way as before. The lcd.write is not a function on <LiquidCrystal_I2C.h>, which is the library I am using for I2C to communicate with led. Adding a '\0' to the end just removed the 4 lines at the end. The RING still appeared – Feeds Nov 24 '20 at 15:50
  • @Feeds, I'm not surprised memmove() made no difference on an AVR. I am sort of surprised you're not able to use that form of write() though, since it is part of the Print class which provides print() and println() among others. Do you have a link to the exact rendition of LiquidCrystal_I2C library you're using? – timemage Nov 24 '20 at 16:18
  • Now that I think about it, the problem may reside on the lcdtext not being a string, but a char array. The library I am using is the one on sketch github.com/johnrickman/LiquidCrystal_I2C, version 1.1.2 – Feeds Nov 24 '20 at 18:35
  • At github.com/johnrickman/LiquidCrystal_I2C/blob/master/… you can see where Print inherited. At github.com/johnrickman/LiquidCrystal_I2C/blob/master/… single character write() is implemented. github.com/arduino/ArduinoCore-avr/blob/1.8.3/cores/arduino/… where the default implementation is for the two-parameter version of write() mentioned uses the single character write(). – timemage Nov 24 '20 at 18:46
  • In other words, it should work fine. If that's a sticking point, take some time with it and if still stuck maybe start a new question with a cut-down code example asking about that one aspect. – timemage Nov 24 '20 at 18:50
0

Huh, I kinda solved it, but I still dont get it how the lcdtext was getting the full string.

I replaced the lcdScreen function with this:

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]);
  }
}

It works, but I still dont understand why I had the full text when I am just calling this function with a single char each time

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.