Go Down

Topic: OLED display not working when potentiometer code is active! (Read 231 times) previous topic - next topic

jaffasplaffa

Hello!

This is my first post in the forum and also my first hardware/Arduino project so bare with me. I'll try to explain it as well as I can.

The project:
I have made an Arduino Mega controller with 8 pots(Alp) and a single 128X64 OLED display. Everything is connected and works on the hardware side. I send data from the Arduino pots to Pure Data, which works. And I also send some strings from Pure Data to Arduino and then parse them on the Arduino to display them on the OLED. Ar first I had the OLED connected to 5v along the pots, but that created some pot jitter. So I moved the display to 3.3v, that removed the pot jitter. Is that okay to d that? Or maybe it's part of the issue I am having. I need to test that tommorow, now that I think of it.

But on the software side, I am having another issue.

I can load the code for the pots and the code for the display individually and they work fine.

But when I add the code for both pots and OLED to the same sketch, the OLED is not updated with the incoming string values, only the pots are working.

Been trying all day and I think I could use some tips here. I am not a programmer, just a hobbyist so any tips are appreciated :)

I tried changing the baudrate both up and down and in some cases nothing works. At really baudrate, like 230400, which I think is the highest the Pure Data Comport object accepts it updates the display but it happens very very slowly. But since I can't set the baudrate higher than 230400 I think I need to find another way.

I also tried moving the pot code after the OLED code and that doesn't change anything.

Is there any obvious mistakes here that I could change?

Thanks in advance ;)

Code: [Select]
/*********************************************************************
Alexandros Drymonits pot library - Declare
*********************************************************************/
// analog_values array size, must be constant
const int num_of_analog_pins = 8;
// digital_values array size, must be constant
const int num_of_digital_pins = 0;

// create an array to store the values of the potentiometers
int analog_values[num_of_analog_pins];
// create an array to store the values of the push-buttons
int digital_values[num_of_digital_pins];

// Old analog values for deadband
int old_analog_values[num_of_analog_pins];
/*********************************************************************
Adafruit SH1106 (Some functions not available, like for SSD1306) - Declare
*********************************************************************/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#define OLED_RESET -1
Adafruit_SH1106 display(OLED_RESET);
/*********************************************************************
Parse incoming string - Declare
*********************************************************************/
String readString; //main captured String
String param1,param2,param3,param4,param5,param6,param7,param8;
int index1,index2,index3,index4,index5,index6,index7,index8;

/*********************************************************************
Void setup
*********************************************************************/

void setup()   {             
 
  // Alexandros Drymonits pot library 
  for(int i = 0; i < num_of_digital_pins; i++){
  //  pinMode((i + 2), INPUT_PULLUP);
  pinMode((i + 2), INPUT);                    }
   
  Serial.begin(57600); // 57600 // 38400 // 74880
 
  // SH1106 Adafruit display library
  display.begin(SH1106_SWITCHCAPVCC, 0x3C);  // I2C addr 0x3C(Check with Ic2 scanner)
  display.clearDisplay(); // Clear the buffer/display.
  delay(500);
 
  } // Setup end


/*********************************************************************
Void loop
*********************************************************************/

void loop() {

/*********************************************************************
Serial output to Pure Data
*********************************************************************/

 
  // Alexandros Drymonit's pot library
  // Analog pins
  for(int i = 0; i < num_of_analog_pins; i++) analog_values[i] = analogRead(i);
 
  // add some deadband
  for(int i = 0; i < num_of_analog_pins; i++){
  int dbRange = 5;
  if (analog_values[i] < (old_analog_values[i] - dbRange) || analog_values[i] > (old_analog_values[i] + dbRange))
  { old_analog_values[i] = analog_values[i];   }

  // Tag for analog params
  Serial.print("Analog_values: "); 
  // Analog params send over serial
  for(int i = 0; i < (num_of_analog_pins - 1); i++){   
  Serial.print(old_analog_values[i]);
  Serial.print(" ");
  }
  Serial.println(old_analog_values[num_of_analog_pins - 1]);
  }
 
  // Digital pins
  // for(int i = 0; i < num_of_digital_pins; i++) digital_values[i] = !digitalRead(i + 2); // on/off
  for(int i = 0; i < num_of_digital_pins; i++) digital_values[i] = digitalRead(i + 2); // off/on

  Serial.print("Digital_values: ");
  for(int i = 0; i < (num_of_digital_pins - 1); i++){
  Serial.print(digital_values[i]);
  Serial.print(" ");
  }
  Serial.println(digital_values[num_of_digital_pins - 1]);
 

/*********************************************************************
Serial input From Pure data - To display
*********************************************************************/

  display.clearDisplay(); // Clear display on every loop

  // https://forum.arduino.cc/index.php?topic=336753.0
  // Parse an incoming string, so you can use values seperately
  // Expect a string like: 90:low:15.6:125* or 130:hi:7.2:389*
  // The : is for seperating the string
  // The * at the end is for stopping the process
   
  if (Serial.available())  { // Only update when serial data is received
  char c = Serial.read();    // get one byte from serial buffer
  if (c == '*') {            // * is the end of the string
     
  index1 = readString.indexOf(':');                         // finds location of 1. ,
  param1 = readString.substring(0, index1);                 // captures 1. data String
  index2 = readString.indexOf(':', index1+1 );              // finds location of 2. ,
  param2 = readString.substring(index1+1, index2);          // captures 2. data String
  index3 = readString.indexOf(':', index2+1 );              // finds location of 3. ,
  param3 = readString.substring(index2+1, index3);          // captures 3. data String
  index4 = readString.indexOf(':', index3+1 );              // finds location of 4. ,
  param4 = readString.substring(index3+1, index4);          // captures 4. data String
  index5 = readString.indexOf(':', index4+1 );              // finds location of 5. ,
  param5 = readString.substring(index4+1, index5);          // captures 5. data String
  index6 = readString.indexOf(':', index5+1 );              // finds location of 6. ,
  param6 = readString.substring(index5+1, index6);          // captures 6. data String
  index7 = readString.indexOf(':', index6+1 );              // finds location of 7. ,
  param7 = readString.substring(index6+1, index7);          // captures 7. data String
  index8 = readString.indexOf(':', index7+1 );              // finds location of 8. ,
  param8 = readString.substring(index7+1, index8-index7-1); // captures 8. data String
 
  // SH1106 Adafruit display library
  display.setTextSize(1);
  display.setTextColor(WHITE,BLACK);
  display.setCursor(0,0);

  display.println("ParA:"+ param1);
  display.println("ParB:"+ param2);
  display.println("ParC:"+ param3);
  display.println("ParD:"+ param4);
  display.println("ParE:"+ param5);
  display.println("ParF:"+ param6);
  display.println("ParG:"+ param7);
  display.println("ParH:"+ param8);

  delay(1);          // Update every x ms
  display.display(); // Update the display - do it EVERYTIME you send something to display!
 
  readString="";     //clears variable for new input
  // param1=""; param2="";  param3="";  param4=""; // Rst, not needed
  }
  else {     
  readString += c; //makes the string readString
  }
  }
/********************************************************************/

} // Loop end



Paul__B

I see "String"s used! (With capital "S".)

Expect pain!

jaffasplaffa

Hey Paul B!

Thank you for the answer. Could you explain what you mean?

Do you have a suggestion for a better way of doing this?

I am sending a string from Pure Data with several numbers, separated by : and then I find the end of the string with the *:
12:2:4:4:23:54:9:43*

I like this method, cause I can basically send anything from floats to strings to integers.

Can I use another variable type instead of "String"?

Thanks!


Paul__B

It is probably not your immediate problem, but using the form of "String" with a capital "S" on a microcontroller generally causes heap overflow and program crashes - after the code has been running for some time.

It is preferred to define and manipulate character arrays allocated only at the beginning and of a fixed size.

david_2018

Since you have num_of_digital_pins defined as 0, I would comment out all the code related to that.

This will not do what you expect - when will the condition i < 0 be true?
Code: [Select]

  for (int i = 0; i < num_of_digital_pins; i++) {
    //  pinMode((i + 2), INPUT_PULLUP);
    pinMode((i + 2), INPUT);
  }


jaffasplaffa

Quote from: Paul__B Sun Apr 04 2021 13:32:22 GMT+0200 (Central European Summer Time)
It is probably not your immediate problem, but using the form of "String" with a capital "S" on a microcontroller generally causes heap overflow and program crashes - after the code has been running for some time.

It is preferred to define and manipulate character arrays allocated only at the beginning and of a fixed size.
Hey Paul B!


I did some research on the String vs. C-string issue and I see what you are saying. I think actually this is my exact problem, that the heap overflows fairly quickly and messes it all up. There was many Strings in my last code, which is noy good I guess.


So I have been researching all weekend and found a lot of different ways of doing it, using c string instead. I got communication from Arduino to Pure Data working fine, but have not managed to get anything proper working yet for Pure Data to Arduino communication.


I found this code, which I have trying to make work, for creating a string from messages send from Pure Data:




Code: [Select]
// https://www.best-microcontroller-projects.com/arduino-string.html
// .Make the string from serial to feed into string parse thing

#define BUF_LEN 40 // Set length of the string you want to read

// void setup (void) {
void setup () {
   Serial.begin(9600);
   Serial.println("Command Interpreter");
}

// void loop(void ) {
void loop( ) {

static char sdata[BUF_LEN], *pSdata=sdata;
byte ch;
int val;

   if (Serial.available()) {
      ch = Serial.read();

      // -1 for null terminator space
      if ((pSdata - sdata)>=BUF_LEN-1) {
         pSdata--;
         Serial.print("BUFFER OVERRUN\n");
      }

      *pSdata++ = (char)ch;

      if (ch=='\r') {  // Command received and ready.

         pSdata--;       // Don't add \r to string.
         *pSdata = '\0';  // Null terminate the string.


           Serial.println(sdata);
         
         
         pSdata = sdata; // Reset pointer to start of string.

      } // if \r
   }  // available
}


It works completely when I use Arduino IDE's own serial monitor and put in a string like this 12:2:34:45, it prints exactly that in the serial monitor. But when I send a string from Pure Data, it acts completely different.


When I send a few string over from Pure Data, it does not print anything, only thing it does it accumulate the strings and make it longer and longer........
1234
5678
........

It just accumulates them and makes them longer once the buffer BUF_LEN of 40 characters is exceeded, I get a BUFFER OVERRUN message. It behaves completely different than when using Serial Monitor input, which I don't understand why.

I did do some testing on how to send data from Pure Data to Arduino so I am fairlt sure I am doing it the right way. But yeah, obviously something is not correct here.

Any tips appreciated!


MAS3

That code is waiting for a \r character, which is a carriage return (also known as 0x0d or 13).
Your serial monitor does send this character (along with another one) when you hit the enter key.
Does your Pure Data contain that character ?

Have a look at "blink without delay".
Did you connect the grounds ?
Je kunt hier ook in het Nederlands terecht: http://arduino.cc/forum/index.php/board,77.0.html

jaffasplaffa

Hmm, yeah that makes sense. 

I have not made any code for PD that uses \r before, I will check up on how to use it.

Getting closer :) 

Thanks for the pointer :) 

jaffasplaffa

Okay, so I tried changing the carrier return identifier to a simple *. Like this:

Code: [Select]

// https://www.best-microcontroller-projects.com/arduino-string.html[color=#222222][/color]
// .Make the string from serial to feed into string parse thing[color=#222222][/color]
[color=#222222][/color]
#define BUF_LEN 40 // Set length of the string you want to read[color=#222222][/color]
[color=#222222][/color]
// void setup (void) {[color=#222222][/color]
void setup () {[color=#222222][/color]
   Serial.begin(9600);[color=#222222][/color]
   Serial.println("Command Interpreter");[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
// void loop(void ) {[color=#222222][/color]
void loop( ) {[color=#222222][/color]
[color=#222222][/color]
static char sdata[BUF_LEN], *pSdata=sdata;[color=#222222][/color]
byte ch;[color=#222222][/color]
int val;[color=#222222][/color]
[color=#222222][/color]
   if (Serial.available()) {[color=#222222][/color]
      ch = Serial.read();[color=#222222][/color]
[color=#222222][/color]
      // -1 for null terminator space[color=#222222][/color]
      if ((pSdata - sdata)>=BUF_LEN-1) {[color=#222222][/color]
         pSdata--;[color=#222222][/color]
         Serial.print("BUFFER OVERRUN\n");[color=#222222][/color]
      }[color=#222222][/color]
[color=#222222][/color]
      *pSdata++ = (char)ch;[color=#222222][/color]
[color=#222222][/color]
      if (ch=='*') {  // Command received and ready.[color=#222222][/color]
[color=#222222][/color]
         pSdata--;       // Don't add \r to string.[color=#222222][/color]
         *pSdata = '\0';  // Null terminate the string.[color=#222222][/color]
[color=#222222][/color]
[color=#222222][/color]
           Serial.println(sdata);[color=#222222][/color]
         [color=#222222][/color]
         [color=#222222][/color]
         pSdata = sdata; // Reset pointer to start of string.[color=#222222][/color]
[color=#222222][/color]
      } // if \r[color=#222222][/color]
   }  // available[color=#222222][/color]
}


This actually works. My string now looks like this:
[Dert:9.2:b:8:c:7:d:1]*


Yes :) 

So next step is to parse that string, so I can set the individual values to my OLED and print it to each it's own line.

Thanks again!

Paul__B

Good thinking!  The delimiter character does not have to be carriage return!

jaffasplaffa

Thanks Paul!

After a while, I also gave up on the update everything method, it's just too laggy to update the whole display in one go. I mean it's fine when changing pages of parameters, but it's not good to update everything every time you edit a single parameter.

So I broke it down into single parameters and copied the code a few times and then I use end markers like * _  ¨ / #   for each parameters end marker. Works perfectly and I am happy and I think it's pretty cheap on the cpu, much better than the String thing I did at first.

I am sure more issues will come up later on, but after 2 weeks of trying to make this work, I am REALLY happy with a stabile working project :) 

So now I have to combine my Arduino to Pd sketch and the Pd to Arduino sketch and see how it all works out.........

MAS3

Please select Copy for Forum in the IDE instead of Copy as HTML.

Quote from: jaffasplaffa
...My string now looks like this:
[Dert:9.2:b:8:c:7:d:1]*
Does it exactly look like that ?
The code shown is supposed to delete the * and replace it with a zero character which is usually used to mark the end of a string.
But if you have the [ and ] in your data, you already have a start and an end character telling you at any time what to expect and when it ends.
(Simply ignore anything that comes in until you see the [ ).


Have a look at "blink without delay".
Did you connect the grounds ?
Je kunt hier ook in het Nederlands terecht: http://arduino.cc/forum/index.php/board,77.0.html

jaffasplaffa

#12
Apr 08, 2021, 02:08 pm Last Edit: Apr 08, 2021, 02:32 pm by jaffasplaffa
Hey Mas3

Thanks for the feedback.

Ups, sorry. I actually posted the code once and saw there was an issue and then I copied it again and it looked fine in the message I was writing, but I see now it looks the same.

For the string, I ditched that method and made a simpler version, that send the value from PD individually, basically dropping the parsing of the incoming serial string. The new method only needs a single delimiter for each value send from pd and it works much better. Like below:

Code: [Select]
#define BUF_LEN 25 // Set length of the string you want to read

static char sdata[BUF_LEN], *pSdata=sdata;
byte ch; 

// SH1106 Adafruit display library dependecies
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH1106.h>
#define OLED_RESET -1
Adafruit_SH1106 display(OLED_RESET);

void setup() {
  Serial.begin(9600); // 9600 // 115200
  display.begin(SH1106_SWITCHCAPVCC, 0x3C);  // I2C addr 0x3C(Check with Ic2 scanner)

// Clear the buffer/display.
  display.clearDisplay();

} // Setup end


void loop() {

   // Serial input from Pure Data
   if (Serial.available()) {
      // display.clearDisplay();
      ch = Serial.read(); // Read the serial input and convert to char
      *pSdata++ = (char)ch;
     
      // Parameter 1 uses *
      if (ch=='*') {  // Command received and ready.

         pSdata--;       // Don't add \r to string.
         *pSdata = '\0';  // Null terminate the string.
           
            Serial.print(sdata); // Remove when done
            display.setTextSize(1);
            display.setTextColor(WHITE,BLACK);
            display.setCursor(0,0);
            display.println(sdata); 
            display.display(); // Update the display - do it EVERYTIME you send something to display!     
            pSdata = sdata; // Reset pointer to start of string.
      }   
}  // Serial-available end
 
} // Loop end


And then for the other parameters I just copy the code and change the delimiter to another, like * + ´ `.

So I do not need the [ ] anymore and they have been dropped, cause that was a general delimiter uses for many parameters and I would like them to be separate, like the above code.

I tried with 4 parameters for now and going to add 4 more today for testing and see how it goes.

MAS3

Make sure that comments are correct.
If you update/change your code containing comments, update those comments too or delete them.
"// Don't add \r to string." is incorrect because you don't have that in your present code.
Leaving this in, will not help you when reviewing this code a few months from today.

I don't know how many parameters you (are planning to) use.
But your code may become quite large this way.
Sometimes it's better to do some of the parsing you just dropped but of course that's up to you.
Whatever you choose to use it'll be a great learning opportunity.

You may want to take an example to NMEA sentences.
They have $ or ! to mark the start of a sentence, followed by a talker ID telling what device is sending this data, and then a sentence ID telling what data to expect.
Next is the the data which is comma separated, and if that's done a * delimiter tells the next 2 characters are the CRC checksum.
There's quite a lot of possible official NMEA sentences including proprietary ones.

It's best to only update any display if you need to do so.
So if there are no changes, do not update the display.
It will save you lots and lots of time, but you have to keep track of what is already displayed and decide whether or not it needs an update.
Displaying "Value 1: "followed by the content of some variable, means you won't need to update the text.
You only need to do an update on the variable shown, if it has changed since the last time you displayed it.
Have a look at "blink without delay".
Did you connect the grounds ?
Je kunt hier ook in het Nederlands terecht: http://arduino.cc/forum/index.php/board,77.0.html

jaffasplaffa

Hey MAS3

Yes, I am cleaning it up as i go. Some of the comments are left for understanding, but yea, eventually they will all be gone :) 

I am planning maybe 8 or 16 parameters. Doing it how I have done in last code section I posted, only updates anything send to the display when it's actually changed, which takes of the pressure. In Pure Data I also use "change" object, so values are only send if changed. When I parsed it, the whole display was updated all the time. Could probably be fixed with some "is new c string == to old c string" and then only update if different. I tried a couple of things and could not make it work, so I did the above instead and it works great for now. And doing it like this is automaitcally only updates any parameter changed, I don't have to do any extra stuff.

Yes you are right, I could probably parse just a couple of params.  I might do that if this doesn't work for many parameters. I will find out when adding more parameters, but for now this is code I understand.

I will check up on the NMEA sentences for sure.

I just tried to make a function for each value, but did not succeed yet. I will try again. I think having it as a function with a char input and another input for the delimiter, would be nice and make code simpler to work with and expand. Or I could probably also throw it in a for loop..... So many options :) 

Thanks again ;) 

Go Up