Main loop... maths bricks the upload

Hello

I have another thread with regards to trying to get a working DMX routine on an ATmega328p... I have kinda solved that.

But, this code here (Yes, its a mess because it's work in progress) has this weird thing I cannot solve.

Near the end of the loop, if I use any kind of maths, bool statement etc, it freezes and will not allow the processor to run (it uploads via USBasp, but never runs).

As it is listed below, the routine works and reads DMX fine.

For instance...
I did have bools to monitor the state of the lights and servo being on/off, but stating servomotor = false would freeze the processor and I just could not work out why.

if ((RXDMXch1 > 128) and (servomotor ==false)) This froze the loop

So I changed that to be if ((RXDMXch1 > 128) and (digitalRead(pin1) == LOW)) and this worked.

But now the statement servopos = servostart stops it running? Eh? (currently commented out)

I think I am well within memory boundaries:
Sketch uses 16062 bytes (52%) of program storage space. Maximum is 30720 bytes.
Global variables use 885 bytes (43%) of dynamic memory, leaving 1163 bytes for local variables. Maximum is 2048 bytes.

So I can only assume I am somehow crashing into something in this horrible, troublesome DMX library?

#include <Wire.h>                                                                                                 // I2C comms
#include <SPI.h>                                                                                                  // Serial comms

#include <DMXSerial.h>                                                                                            // DMX library.

#include <Adafruit_GFX.h>                                                                                         // Graphics library
#include <Adafruit_SSD1306.h>                                                                                     // Oled screen (Adafruit SSD1306.H has list of commands)

#include <EEPROM.h>                                                                                               // EEPROM

#define DMXMODEPIN 17                                                                                             // Define the DMX direction change pin (it's pin 2 default in the DMXSerial library)
#define DMX_USE_PORT0                                                                                             // ATmega328p only has one serial port (0)
#define DMXSPEED 250000L                                                                                          // Already defined in the library

#define SCREEN_WIDTH 128                                                                                          // OLED display width, in pixels
#define SCREEN_HEIGHT 64                                                                                          // OLED display height, in pixels
#define OLED_RESET    -1                                                                                          // Reset pin # (or -1 if sharing Arduino reset pin)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Pin 0 = Serial RX                     Incoming DMX data
// Pin 1 = Serial TX                     Transmitted DMX data
#define pin1 2                           // PCINT18
#define pin3 3                           // PCINT19 - PWM
#define pin4 4                           // PCINT20
#define pin5 5                           // PCINT21 - PWM
#define pin6 6                           // PCINT22 - PWM - AIN0
#define pin7 7                           // PCINT23 - AIN1
#define pin8 8                           // PCINT0
#define pin9 9                           // PCINT1 -  PWM
#define pin10 10                         // PCINT2 -  PWM - SS
// Pin 11 = Mosi                         // Used for programming but actually are available on the programming header
// Pin 12 = Miso                         // Used for programming but actually are available on the programming header
// Pin 13 = SCK                          // Used for programming but actually are available on the programming header
#define selectbutton 14                  // // Onboard/remote SELECT button (PCINT8 - A0 - ADC0)
#define heartbeat 15                     // Onboard LED indicator
#define pin2 16                          // PCINT10 - A2 - ADC2
#define DMXdirection 17                  // HIGH = Transmit.  LOW = Receive
// Pin 18 SDA                            // I2C SDA to onboard OLED screen
// Pin 19 SCL                            // I2C SCL to onboard OLED screen
#define enterbutton A7                   // Onboard/remote ENTER button

unsigned long currentMillis;             // Timing variables
unsigned long heartbeatMillis;

bool ledstatus;                          // Flip/flop for status led

int entervalue;                          // Analogue value from the ENTER button pin

float servopulse = 0.1;                  // How fast the servo sweeps
bool servodir;                           // Flip/flop for servo direction
int servostart = 700;                    // Servo start angle
int servostop = 2300;                    // Servo stop angle
int servopos;                            // The value being sent to the servo
//int servopause = 500;                    // How long the servo pauses for

byte RXDMX1address = 1;                   // DMX variables
byte RXDMX2address = 2;
byte RXDMX3address = 3;
byte RXDMXch1 = 0;
byte RXDMXch2 = 0;
byte RXDMXch3 = 0;
byte oldRXDMXch1 = 0;
byte oldRXDMXch2 = 0;
byte oldRXDMXch3 = 0;

//----------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {
  pinMode (heartbeat, OUTPUT);
  pinMode (DMXdirection, OUTPUT);
  pinMode (enterbutton, INPUT);
  pinMode (selectbutton, INPUT);
  pinMode (pin1, OUTPUT);
  pinMode (pin2, OUTPUT);
  pinMode (pin3, INPUT);
  pinMode (pin4, INPUT);
  pinMode (pin5, INPUT);
  pinMode (pin6, INPUT);
  pinMode (pin7, INPUT);
  pinMode (pin8, INPUT);
  pinMode (pin9, INPUT);
  pinMode (pin10, OUTPUT);

  Wire.begin();

  display.begin(SSD1306_SWITCHCAPVCC, 0x3c);

  DMXSerial.init(DMXReceiver);                      // DMX receiver
  //DMXSerial.init(DMXController);                    // DMX transmitter
  //DMXSerial.init(DMXProbe);                         //
  digitalWrite(DMXdirection, LOW);                  // Enable receive

  drawscreen();
  drawheader();
  delay(3000);
  drawscreen();
  drawRX();

}//------ end of setup---------

void drawscreen() {
  display.clearDisplay();                                                            // Clear the buffer.
  display.drawRoundRect(0, 0, 128, 64, 4, WHITE);                                    // Draws a rectangle with rounded corners.  Starts at 0,0 (top left) and then 128 wide x 64 high with corners rounded to radius 4
  display.drawRoundRect(2, 2, 124, 60, 4, WHITE);
}

void drawheader() {
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.setCursor(18, 36);
  display.print(F("DMX CONTROLLER"));
  display.display();
}

void drawRX() {                                                                      // Draw the DMX receive page
  display.setTextSize(1);
  display.drawRect(14, 6, 102, 12, WHITE);
  display.setCursor(18, 8);
  display.print(F("DMX RECEIVE MODE"));

  display.fillRect(98, 23, 20, 37, BLACK);

  display.setCursor(10, 24);
  display.print(F("DMX 1 RX Value: "));
  display.setCursor(100, 24);
  display.print(RXDMXch1);

  display.setCursor(10, 36);
  display.print(F("DMX 2 RX Value: "));
  display.setCursor(100, 36);
  display.print(RXDMXch2);

  display.setCursor(10, 48);
  display.print(F("DMX 3 RX Value: "));
  display.setCursor(100, 48);
  display.print(RXDMXch3);

  display.display();
}

//#####################################################################################################################

void loop() {

  currentMillis = millis();

  if (currentMillis - heartbeatMillis > 1000) {                              // Once a second heartbeat
    if (ledstatus == false) {
      ledstatus = true;
      heartbeatMillis = currentMillis;
      digitalWrite(heartbeat, HIGH);
    }
    else
    {
      ledstatus = false;
      heartbeatMillis = currentMillis;
      digitalWrite(heartbeat, LOW);
    }
  }

  //--------------------------------------------- Buttons/Inputs ----------------------------------------------

  entervalue = analogRead(enterbutton);
  if (entervalue > 800) {                                                    // The ENTER button is analogue only
    display.print(F("ENTER"));
    display.display();
    do {
      entervalue = analogRead(enterbutton);
    } while (entervalue > 800);
  }

  if (digitalRead(selectbutton) == HIGH) {
    display.print(F("SELECT"));
    display.display();
    do {} while (digitalRead(selectbutton) == HIGH);
  }

  /*
    if (digitalRead(pin1) == HIGH) {
      display.print(F("1"));
      display.display();
    }

      if (digitalRead(pin2) == HIGH) {
        display.print(F("2"));
        display.display();
      }
      if (digitalRead(pin3) == HIGH) {
        display.print(F("3"));
        display.display();
      }
      if (digitalRead(pin4) == HIGH) {
        display.print(F("4"));
        display.display();
      }
      if (digitalRead(pin5) == HIGH) {
        display.print(F("5"));
        display.display();
      }
      if (digitalRead(pin6) == HIGH) {
        display.print(F("6"));
        display.display();
      }
      if (digitalRead(pin7) == HIGH) {
        display.print(F("7"));
        display.display();
      }
      if (digitalRead(pin8) == HIGH) {
        display.print(F("8"));
        display.display();
      }
      if (digitalRead(pin9) == HIGH) {
        display.print(F("9"));
        display.display();
      }
      if (digitalRead(pin10) == HIGH) {
        display.print(F("10"));
        display.display();
      }
  */


  //------------------------------------------------------ DMX --------------------------------------------------------

  unsigned long lastPacket = DMXSerial.noDataSince();                                               // Calculate how long no data packet was received

  if (lastPacket < 6000) {                                                                          // Read DMX value
    RXDMXch1 = DMXSerial.read(RXDMX1address);
    RXDMXch2 = DMXSerial.read(RXDMX2address);
    RXDMXch3 = DMXSerial.read(RXDMX3address);
  } else {                                                                                          // Display DMX Receive error
    delay(200);
    drawscreen();
    display.setCursor(11, 23);
    display.setTextSize(2);
    display.print(F("DMX ERROR"));
    display.display();
    delay(1000);
    drawscreen();
    drawRX();
  }

  if ((RXDMXch1 != oldRXDMXch1) or (RXDMXch2 != oldRXDMXch2) or (RXDMXch3 != oldRXDMXch3)) {       // Update the screen and any outputs
    drawRX();
    oldRXDMXch1 = RXDMXch1;
    oldRXDMXch2 = RXDMXch2;
    oldRXDMXch3 = RXDMXch3;
  }

  if ((RXDMXch1 > 128) and (digitalRead(pin1) == LOW)) {                                             // Turn on the light
    digitalWrite(pin1, HIGH);
  }

  if ((RXDMXch1 <= 128) and (digitalRead(pin1) == HIGH)) {
    digitalWrite(pin1, LOW);
  }

  if ((RXDMXch2 > 128) and (digitalRead(pin2) == LOW)) {                                            // Turn on the servo power
    digitalWrite(pin2, HIGH);
  }

  if ((RXDMXch2 <= 128) and (digitalRead(pin2) == HIGH)) {
    digitalWrite(pin2, LOW);
  }


  // Introduce a millis delay here to create a pause

  if ((RXDMXch3 > 0) and (digitalRead(pin1) == HIGH)) {

   if (servopos < (servostart + 1)) {                         // Limit the start position and flip the direction
      servodir = true;
      //servopos = servostart; // ###################### WTF #################
    }
    if (servopos > servostop) {                              // Limit the stop position and flip the direction
      servodir = false;
      //servopos = servostop;
    }
  }
/*
  servopulse = RXDMXch3 / 10;                                // Slow up the 0-255 variable

  if (servodir == true) {                                    // Sweep up
    servopos = servopos + servopulse;

    // Graphics here
  }

  if (servodir == false) {                                   // Sweep back
    servopos = servopos - servopulse;

    // Graphics here
  }
*/


}
//#####################################################################################################################

More likely it's dynamic allocation in something like the GFX library that's causing you grief (or both GFX and DMX)

How do I find that? Can I find that!

Well, a 128x64 monochrome screen is going to consume 8192 bits, or 1k bytes, of the 885 bytes of spare RAM you've got.

Hmm. thought it allocated enough memory in advance. Interesting. Never thought to check numbers.

Think I need a smaller screen library, trouble is I need simple graphics capabilities.

I have been playing around with other text libraries for the SSD1306

I thought the u8x8 and U8g2 libraries were supposed to be light?
When I upload them to my Atmega328p, basic test uses 88% of the program space? Twice the amount the Adafruit.GFX library took.

Look like it's time to walk away from this chip, as it clearly isn't suitable for this (even for a basic task). Shame, the PCB came out quite nice

Not all screens/libraries need a screen buffer....
You can directly write to the screen instead...

@build_1971 - why are you telling me this?
Do the Adafruit GFX / 1306 libraries do this?

Because you are leaving OP hopeless...

I prefer "aspirational"

I had to look up that word in the dictionary...

'Walk away from this chip" does not show much aspiration...

So I do not really get what you mean.

That can be very dependent on the sketch, and which/how many fonts are being used. In the case of U8g2, there is a tradeoff between smaller ram usage and slightly more code when using a paged buffer. If you are really close to running out of program memory, the github download for U8g2 includes all the font source files and code to generate custom font files with only the characters you will be using.

I think you must be mistaken - where did I say/imply that?

had a similar issue..
ended up using..
SSD1306Ascii
wasn't doing any graphics though, so not sure if it will be correct fit..
it is small.. :slight_smile:

That is a quote from OP...

So, s/he didn't take the hint (to look for another solution ... or, state the requirements to allow others to guide to another solution).

Its the u8x8 Fonts. Some of them are huge (as in taking 40% of the program space).
Binned all graphics, found a tiny footprint font and got it working at a basic level.
But it's not good enough, so redesigning the whole pcb with a ESP32 instead now.

If you state it uses 1 bit per pixel in ram, then it uses a screenbuffer. Maybe it can also be used without that buffer.
In case of screen buffer, you prepare a screenbuffer or canvas and than copy that to the screen as a whole.
In the case without screen buffer, you write directly to the screen. If you want to erase things, you can redraw them in background color. If you have overlapping objects, that might yield holes in your graphics.
Or, you clear the screen and do a full redraw. That will lead to flickering.
So OP needs to decide if he/she wants to try without screen buffer or wants to wait for his/her new board.
Maybe his graphics have many overlaps. Or many changing things. That would make it quite difficult.
A chart with axis, ticks and some (changing) data points is well doable. If you add auto scaling on both axis, it will be pretty difficult.

@build_1971 - you forgot the case of where you have a partial screen buffer (a fraction of the size of the entire screen), but have to run through the entire display list multiple times to draw the whole screen.
This reduces the RAM usage, at the expense of a much slower update rate.

Or the case (very unlikely here) where you have a distributed frame buffer, each with its own processor...

She is up and running, but I have had to kerb my targets.
Originally I had hoped to have receive and transmit on the same board (it has optically isolated DMX comms with a software controlled direction switch).
But, the ATmega328p just isn't up to it with the libraries available. Spent far too many hours on this already. There are a lot of pin and memory conflicts to deal with. I am not that level of a 'coder'.
Maybe someone with better knowledge would get it working, but I can't.

The original design had servo outputs on the pins, with a graphical representation of the servo angle (there was a reason for that).
But, the servo library (or any other) needs a clock and they are all used with the DMX timings (tried all the libraries).
I could move the servo using simple digitalwrites and some suspect timing, but it's a bit clunky.
And as we know, the graphics library is just a memory step too far.

So, I will use these boards as either/or.

Transmit module:
They have 10 pins that can be programmed to transmit 10 different DMX values in either toggle or latch mode for a pulse or a held on delay (all set up in a menu).

Receive module:
A valid DMX value on a preset channel will make one of the 10 pins go high. Again, all setup in the menu.

Hoping the ESP32 board will give me more options.