API communication slow using messageport

Support for Arduino in combination with Air Manager and Air Player

Moderators: russ, Ralph

Message
Author
JackZ
Posts: 2262
Joined: Mon Feb 22, 2016 1:02 pm

Re: API communication slow using messageport

#11 Post by JackZ »

Considering the low prices of these screens I would definitely consider switching over an SPI version of your screen instead of an I2C interface.
Make sure that the SPI interface is the one with 5 data pins +2 for voltage and GND (7 pins total)The other advantage is that SPI allows you to use multiple screens at the same time (multiplexing).

And try to use 3.3V instead of 5V for at least the VIN (VCC) pin, as the IC Driver of the screen seems to require it, even if the TTL command levels for the data pins seem to be 5V tolerant though.

You can use the 3.3V output pin from the Arduino
In my project I use 4 SPI OLEDs with Message Port and u8g2 library with a custom font, without issue.
5F3374CF-1E11-4189-B487-895FECB8C50C.jpeg
Jacques
My YouTube Chanel on the A320 (Real SOPs by an Airline Pilot IRL):
https://www.youtube.com/playlist?list=P ... 0Q6SBASRqJ

User avatar
jph
Posts: 2846
Joined: Fri Apr 10, 2020 12:50 pm
Location: Somewhere over the rainbow..

Re: API communication slow using messageport

#12 Post by jph »

Shimokuta wrote: Wed Jun 30, 2021 3:04 pm I am using a 1.54" I2C Oled display, similar to this one
https://nl.aliexpress.com/item/10050020 ... 4c4djIxXgv

I tried reducing the messageport ticks in my Arduino sketch, but this did even caused more latency!!
So i increased the messageport ticks and this did speed up the interfacing!!
I is acceptable now...but not as i would like

Maybe using the u8g2 library also will help.
I will try this too.
HI, that is not what I asked you to try. I said try it with an Arduino ONLY sketch - no messageport. :shock:
You need to start at the basics which create a baseline from which to work from. Once you get the best response on pure Arduino, then - and only then - add the messageport lib, and only then. That way, you can compare any degradation in performance if it exists and correct where necessary. ;)
Joe
Joe. CISSP, MSc.

Shimokuta
Posts: 113
Joined: Wed Feb 03, 2021 12:52 pm

Re: API communication slow using messageport

#13 Post by Shimokuta »

Yes that is probably i good idea too.
I will write a test script to identify where the lag is comming from.

User avatar
jph
Posts: 2846
Joined: Fri Apr 10, 2020 12:50 pm
Location: Somewhere over the rainbow..

Re: API communication slow using messageport

#14 Post by jph »

Shimokuta wrote: Fri Jul 02, 2021 10:59 am Yes that is probably i good idea too.
I will write a test script to identify where the lag is comming from.
That would be great ;)
It is always the best way to start as you can narrow down any issues. :)
Let us know how you go on.
Joe
Joe. CISSP, MSc.

Shimokuta
Posts: 113
Joined: Wed Feb 03, 2021 12:52 pm

Re: API communication slow using messageport

#15 Post by Shimokuta »

hmm , i did not see any different behavious in speed with or without the messageport
As soon as i change the font to a more larger font there is lag when turning the encoder.
I suspect the I2C interface , but am not sure of course
Here is my test script with font DSEG7_Classic_Regular_16

i am satisfied for now with the lag, but i think it could be faster.

Code: Select all

#include "Arduino.h"
#include "NewEncoder.h"
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

#include <Fonts/DSEG7_Classic_Regular_16.h>

void ESP_ISR callBack(NewEncoder *encPtr, const volatile NewEncoder::EncoderState *state, void *uPtr);

// Pins 2 and 3 should work for many processors, including Uno. See README for meaning of constructor arguments.
// Use FULL_PULSE for encoders that produce one complete quadrature pulse per detnet, such as: https://www.adafruit.com/product/377
// Use HALF_PULSE for endoders that produce one complete quadrature pulse for every two detents, such as: https://www.mouser.com/ProductDetail/alps/ec11e15244g1/?qs=YMSFtX0bdJDiV4LBO61anw==&countrycode=US&currencycode=USD
NewEncoder encoder1(2, 3, 118, 136, 0, FULL_PULSE);
NewEncoder encoder2(18, 19, 118, 136, 5, FULL_PULSE);

int16_t prevEncoderValue;
volatile NewEncoder::EncoderState newState;
volatile bool newValue = false;

void setup() {

   display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
   display.setTextSize(1);
   display.setTextColor(WHITE, BLACK);
   display.clearDisplay();
   display.setFont(&DSEG7_Classic_Regular_16);
  
  NewEncoder::EncoderState state;

  Serial.begin(115200);
  delay(2000);
  Serial.println("Starting");
  if (!encoder1.begin()) {
    Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
    while (1) {
      yield();
    }
  } else {
    encoder1.getState(state);
    Serial.print("Encoder Successfully Started at value = ");
    prevEncoderValue = state.currentValue;
    Serial.println(prevEncoderValue);
  }
  encoder1.attachCallback(callBack);

if (!encoder2.begin()) {
    Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
    while (1) {
      yield();
    }
  } else {
    encoder2.getState(state);
    Serial.print("Encoder Successfully Started at value = ");
    prevEncoderValue = state.currentValue;
    Serial.println(prevEncoderValue);
  }
  encoder2.attachCallback(callBack);
  
}

void loop() {

  int16_t currentValue;
  NewEncoder::EncoderClick currentClick;

  if (newValue) {
    noInterrupts();
    currentValue = newState.currentValue;
    currentClick = newState.currentClick;
    newValue = false;
    interrupts();
    Serial.print("Encoder: ");
    if (currentValue != prevEncoderValue) {
      Serial.println(currentValue);
         
      prevEncoderValue = currentValue;
    } else {
      switch (currentClick) {
        case NewEncoder::UpClick:
          Serial.println("at upper limit.");
          //prevEncoderValue = 118;
          break;

        case NewEncoder::DownClick:
          Serial.println("at lower limit.");
         // prevEncoderValue = 136;
          break;

        default:
          break;
      }
    }
  }
   
   display.setCursor(0,20);
   display.print(currentValue);
   display.display();
   display.clearDisplay();
  
}
   
void ESP_ISR callBack(NewEncoder *encPtr, const volatile NewEncoder::EncoderState *state, void *uPtr) {
  (void) encPtr;
  (void) uPtr;
  memcpy((void *)&newState, (void *)state, sizeof(NewEncoder::EncoderState));
  newValue = true;
}

User avatar
jph
Posts: 2846
Joined: Fri Apr 10, 2020 12:50 pm
Location: Somewhere over the rainbow..

Re: API communication slow using messageport

#16 Post by jph »

Hi,

It is early, so you have to allow for that, and last night was a good night out...
And yes, I hold my hand up, there were some delay() used - but purely for testing - idleness. so forgive me for that.
Ignore the needle arc math.. I can hardly remember most of it now haha... :shock: and, my commenting was pretty poor as it was a lash-up for my use only. It is the principle of the blank and draw that i am referring to as opposed to a clear buffer.

this is the display type !
1306 oled.jpg
1306 oled.jpg (9.43 KiB) Viewed 3388 times
Try not clearing the display buffer every time. This causes huge lag.
I was looking at some old code I did for a Turbo boost gauge for my Land Rover on a 1306 (SPI) with Adafruit lib.
What I did was to rewrite the needle in the gauge display by 'blanking' the original needles by changing to the background colour, then redrawing with the foreground colour.
A super smooth display.
I am sure this worked for the text as well but it was a while back and I don't have a 1306 at hand. I have a few somewhere..... :? I found some old code where you can see the foreground background shift.
You can probably run it on your display ??? - ah, maybe not as yours is I2C, but, you can probably alter the display type for the lib for testing.

Code: Select all

// test of turbo boost gauge basic principles for Land Rover Freelander . Joe Hanley 2019

//---------------------------------------------------------------------------------------------------------------------------------------------------------
// includes 
//---------------------------------------------------------------------------------------------------------------------------------------------------------
#include <SPI.h>                      // Arduino SPI Library
#include <Adafruit_GFX.h>             // OLED graphics library by Adafruit
#include <Adafruit_SSD1306.h>         // SSD1306 0.96" OLED Drivers by Adafruit

//---------------------------------------------------------------------------------------------------------------------------------------------------------
// constants
//---------------------------------------------------------------------------------------------------------------------------------------------------------
//// using software SPI 
//const int OLED_MOSI  =  9;           // SDA on display
//const int OLED_CLK   = 10;           // SCL on display
//const int OLED_DC    = 11;           // DC  on display
//const int OLED_CS    = 12;           // not used on this display (128 x 64 oled SSD1306 - ebay)
//const int OLED_RESET = 13;           // RST on display

//using hardware SPI test
// using software SPI 
const int OLED_MOSI  =  11;           // SDA on display
const int OLED_CLK   =  13;           // SCL on display
const int OLED_DC    =   8;           // DC  on display
const int OLED_CS    =  10;           // not used on this display (128 x 64 oled SSD1306 - ebay)
const int OLED_RESET =  7;           // RST on display

//Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS);
               

float ltx = 0;    // Saved x coord of bottom of needle
uint16_t osx = 64, osy = 64; // Saved x & y coords
uint32_t updateTime = 0;       // time for next update

int old_analog =  0; // Value last displayed



void setup(void) {
//  // by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
  display.begin(SSD1306_SWITCHCAPVCC);
  display.clearDisplay();
//  // init done

////  Clear the buffer.
//  display.clearDisplay();
// 
//  //start up logo and info
//  display.setTextSize(3);
//  display.setTextColor(WHITE);
//
//  // opening title screens 
//  display.setCursor(19,0);
//  display.println("BOOST");   
//  display.setCursor(18,27); 
//  display.println("GAUGE");
//  display.setTextSize(1);
//  display.setCursor(1,55);
//  display.println("By Joe_Hanley (Joe_H)");
//  display.display();
//  delay(3000);  
////  Clear the buffer.
//  display.clearDisplay();
  
 updateTime = millis(); // Next update time
 drawGaugeArc(90,75,3);   // parameters passed are y center point of arc, radius, thickness in pixels and y+n 
// display.setTextSize(1);
// display.setTextColor(WHITE);
// display.setCursor(1,5);
// display.println("PSI");
// display.display();
}


void loop() {
  if (updateTime <= millis()) {
    updateTime = millis() + 500;

    int reading = 0;
    reading = random(-50, 151); // Test with random value
    //reading = map(analogRead(A0),0,1023,0,100); // Test with value form Analogue 0
    //int reading = 50;

    //reading = 0;
    plotNeedle(reading, 8); // Update analogue meter, 8ms delay per needle increment
    //delay(100);
  }
}




// #########################################################################
// Update needle position
// This function is blocking while needle moves, time depends on ms_delay
// 10ms minimises needle flicker if text is drawn within needle sweep area
// Smaller values OK if text not in sweep area, zero for instant movement but
// does not look realistic... (note: 100 increments for full scale deflection)
// #########################################################################
void plotNeedle(int value, byte ms_delay)
{

  if (value < -10) value = -10; // Limit value to emulate needle end stops
  if (value > 110) value = 110;

  // Move the needle util new value reached
  while (!(value == old_analog)) {
   if (old_analog < value) old_analog++;
   else old_analog--;
//    
    if (ms_delay == 0) old_analog = value; // Update immediately id delay is 0
    
    float sdeg = map(old_analog, -10, 110, -150, -30); // Map value to angle 
    // Calcualte tip of needle coords
    float sx = cos(sdeg * 0.0174532925);
    float sy = sin(sdeg * 0.0174532925);

    // Calculate x delta of needle start (does not start at pivot point)
    float tx = tan((sdeg+90) * 0.0174532925);
    int yPos  = 70; //increase to lower needle centre
    int syRad = 90; //increase to descrease radius (height from top of screen)
//    // Erase old needle image
   display.drawLine(64 + 20 * ltx - 1, yPos, osx - 1, osy, BLACK);
   display.drawLine(64 + 20 * ltx, yPos, osx, osy, BLACK);
   display.drawLine(64 + 20 * ltx + 1, yPos, osx + 1, osy, BLACK);
    
    // Re-plot text under needle
    display.setTextColor(WHITE);
     
    // Store new needle end coords for next erase
    ltx = tx;
    osx = sx * 65 + 64;
    osy = sy * 65 + syRad;
    
    // Draw the needle in the new postion, magenta makes needle a bit bolder
    // draws 3 lines to thicken needle
    display.drawLine(64 + 20 * ltx - 1, yPos, osx - 1, osy, WHITE);
    display.drawLine(64 + 20 * ltx, yPos, osx, osy,WHITE);
    display.drawLine(64 + 20 * ltx + 1, yPos, osx + 1, osy,WHITE);
    display.display();
    // Slow needle down slightly as it approaches new postion
    if (abs(old_analog - value) < 10) ms_delay += ms_delay / 5;
    
    // Wait before next update
    delay(ms_delay);
  }
}

void drawGaugeArc(int arcY,int arcRad,int arcThickness) {    // pass start point
    // Draw the Arc of the Gauge 
    
    for (int radius = arcRad; radius < arcRad + arcThickness ; radius++){                // yStart is centre point for arc, number in loop moves centre point 1 pixel hence greater number thicker line.
        display.drawCircle(64,arcY,radius,WHITE);    
    }
    // Arc draw finished  
}





I also have some pretty neat encoder interrupt code that appears to be far simpler than yours and only uses one interrupt pin per encoder (as that is all that is needed) - and no encoder library.
Joe. CISSP, MSc.

User avatar
jph
Posts: 2846
Joined: Fri Apr 10, 2020 12:50 pm
Location: Somewhere over the rainbow..

Re: API communication slow using messageport

#17 Post by jph »

Also, a small example of using an encoder with interrupts. You only need to use one of the available interrupt pins per encoder and simply 'look' at the 'other' pin as a standard I/O during an interrupt. - note :lol: this one IS properly commented.. ;)
This works fine for 99% of cases for OUR use.
This is a case of where you make the interrupt fit the usage - ie - you are not really expecting another interrupt.. if so, you can use int dis and int en etc. No lib required.
Joe.

Code: Select all

// Joe Hanley 3 2020 Interrupts -
//------------------------------------------------------------------------------
//Test of using interrupts on UNO
//PINS for various boards -
//Uno, Nano, Mini, other 328-based 2, 3
//Mega, Mega2560, MegaADK 2, 3, 18, 19, 20, 21
//Micro, Leonardo, other 32u4-based 0, 1, 2, 3, 7
//Due all digital pins
//------------------------------------------------------------------------------
//Typically global variables are used to pass data between an ISR and the main program. 
//To make sure variables shared between an ISR and the main program are updated correctly, declare them as volatile.
//attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) (recommended)
//attachInterrupt(interrupt, ISR, mode) (not recommended)
//attachInterrupt(pin, ISR, mode) (Not recommended. Additionally, this syntax only works on Arduino SAMD Boards, Uno WiFi Rev2, Due, and 101.)
//Parameters
//interrupt: the number of the interrupt. Allowed data types: int.
//pin: the Arduino pin number.
//ISR: the ISR to call when the interrupt occurs; this function must take no parameters and return nothing. This function is sometimes referred to as an interrupt service routine.
//mode: defines when the interrupt should be triggered. Four constants are predefined as valid values:

//LOW            to trigger the interrupt whenever the pin is low
//CHANGE         to trigger the interrupt whenever the pin changes value
//RISING         to trigger when the pin goes from low to high,
//FALLING        for when the pin goes from high to low.

//The Due, Zero and MKR1000 boards allow also:
//HIGH to trigger the interrupt whenever the pin is high.

// priority
//BOARD                             INT.0 INT.1 INT.2 INT.3 INT.4 INT.5
//Uno, Ethernet                       2     3
//Mega2560                            2     3    21    20    19    18
//32u4 based (e.g Leonardo, Micro     3     2    0     1     7

//detachInterrupt(digitalPinToInterrupt(pin)) (recommended) - disable interrupt  - more if used multiple times ?
// see https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

// and http://gammon.com.au/interrupts
//------------------------------------------------------------------------------

//we use an Arduino Uno here with pins 2 and 3 as int0 and int1
//*************************************************************************************************************************

// Used for generating interrupts using CLK signal
const int PinA = 3;

// Used for reading DT signal
const int PinB = 4;

// Used for the push button switch
const int PinSW = 8;

//// Keep track of last rotary value
//int lastCount = 50;
//
//// Updated by the ISR (Interrupt Service Routine)
//volatile int virtualPosition = 50;

// Keep track of last rotary value
int lastCount = 0;

// Updated by the ISR (Interrupt Service Routine)
volatile int virtualPosition = 0;

// ------------------------------------------------------------------
// INTERRUPT     INTERRUPT     INTERRUPT     INTERRUPT     INTERRUPT
// ------------------------------------------------------------------
void isr ()  {
  static unsigned long lastInterruptTime = 0;
  unsigned long interruptTime = millis();

  // If interrupts come faster than 5ms, assume it's a bounce and ignore
  if (interruptTime - lastInterruptTime > 4) {
    if (digitalRead(PinB) == LOW)
    {
      virtualPosition-- ; // Could be -5 or -10
    }
    else {
      virtualPosition++ ; // Could be +5 or +10
    }

    // Restrict value from 0 to +100
    //virtualPosition = min(100, max(0, virtualPosition));

  }
  // Keep track of when we were here last (no more than every 5ms)
  lastInterruptTime = interruptTime;
}

// ------------------------------------------------------------------
// SETUP    SETUP    SETUP    SETUP    SETUP    SETUP    SETUP
// ------------------------------------------------------------------
void setup() {
  // Just whilst we debug, view output on serial monitor
  Serial.begin(115200);

  // Rotary pulses are INPUTs
  pinMode(PinA, INPUT);
  pinMode(PinB, INPUT);

  // Switch is floating so use the in-built PULLUP so we don't need a resistor
  pinMode(PinSW, INPUT_PULLUP);

  // Attach the routine to service the interrupts
  attachInterrupt(digitalPinToInterrupt(PinA), isr, LOW);
//  attachInterrupt(digitalPinToInterrupt(PinA), isr, FALLING); // falling seems to ause errors distinguishing between up / down
  // Ready to go!
  Serial.println("Start");
}

// ------------------------------------------------------------------
// MAIN LOOP     MAIN LOOP     MAIN LOOP     MAIN LOOP     MAIN LOOP
// ------------------------------------------------------------------
void loop() {


  // If the current rotary switch position has changed then update everything
  if (virtualPosition != lastCount) {

    // Write out to serial monitor the value and direction
    Serial.print(virtualPosition > lastCount ? "Up  :" : "Down:");
    Serial.println(virtualPosition);

    // Keep track of this new value
    lastCount = virtualPosition ;
  }

}
Last edited by jph on Fri Jul 09, 2021 10:58 am, edited 1 time in total.
Joe. CISSP, MSc.

JackZ
Posts: 2262
Joined: Mon Feb 22, 2016 1:02 pm

Re: API communication slow using messageport

#18 Post by JackZ »

Encoders decoding can be done without interrupt I guess by looking at the time difference between two clics. And this will allow for some software denounce as well.
My YouTube Chanel on the A320 (Real SOPs by an Airline Pilot IRL):
https://www.youtube.com/playlist?list=P ... 0Q6SBASRqJ

User avatar
jph
Posts: 2846
Joined: Fri Apr 10, 2020 12:50 pm
Location: Somewhere over the rainbow..

Re: API communication slow using messageport

#19 Post by jph »

JackZ wrote: Wed Jul 07, 2021 10:55 am Encoders decoding can be done without interrupt I guess by looking at the time difference between two clics. And this will allow for some software denounce as well.
Absolutely, there are different ways. but the interrupt is so simple and only requires one interrupt pin per encoder so the mega can service 6 encoders on interrupts.
The important thing is to recognise a state change on a single pin and then look at the other pin. In this way you can always determine the direction. Doing it by time polling is hard work really.
Also, the 'difference in time' between state change of the two pins is not important (or indeed, relevant at all), it is the difference in the state of logic at the time that the encoder is looked at. That is really all that matters. Debounce is a case of ignoring superfluous inputs. - usually by simply ignoring any state change within a certain time frame in mS.
Joe
Joe. CISSP, MSc.

User avatar
jph
Posts: 2846
Joined: Fri Apr 10, 2020 12:50 pm
Location: Somewhere over the rainbow..

Re: API communication slow using messageport

#20 Post by jph »

Just on another note. The Arduino DUE is a bit of an amazing device for interrupts - and most other things. Probably the best all round board for stacks of I/Os of all kinds. Mega footprint but so different internally. - also incredibly fast, stacks of memory - flash and ram -and 32 bits. A powerhouse.
You can assign an interrupt to any of 54 I/O pins.
It is not quite clear as to how many interrupt pins in use you can actually have at one time but seems to be 30 as per the datasheet for the ATMEL SAM uP. - although there is an interpretation that the SAM upD can run 60 due to two registers. Still mindblowing which ever.
All fully Arduino IDE compliant.
They are not too expensive either at around €20 including shipping.
https://www.aliexpress.com/item/1005001 ... 2ff858c9-4
DONT buy the overpriced arduino store unit. It is no better in any way. The design is fully opensource. The only thing in the store is the logos are trademarked - NOT the board design.
You can also use it as a game controller HID device - AND as a messageport device at the same time if you want to be adventurous ;) - and it is a powerhouse of a unit and a 32 bit arm processor.
It is not directly supported by AM, but is fully compatible with messageport and also Arduino IDE.

Joe.
Joe. CISSP, MSc.

Post Reply