I have the following robot-eye-animation code that I'm trying to debug. It runs on an official Arduino UNO R3 if the first line (#define DEBUGGING) is set to 0, but if I set it to 1 (which enab les some print/println around lines 209-228) it fails to allocate the SSD1306 device.
As a background, I'm trying to determine why eyeLevel never reaches a value > 11, but that's not material to the question.
When I compile this with DEBUGGING set to 1 (the scenario that doesn't work), I have the following result:
Sketch uses 21336 bytes (66%) of program storage space. Maximum is 32256 bytes.
Global variables use 807 bytes (39%) of dynamic memory, leaving 1241 bytes for local variables. Maximum is 2048 bytes.
/home/dennis/.arduino15/packages/arduino/tools/avrdude/8.0.0-arduino1/bin/avrdude -C/home/dennis/.arduino15/packages/arduino/tools/avrdude/8.0.0-arduino1/etc/avrdude.conf -v -patmega328p -carduino -P/dev/ttyACM0 -b115200 -D -Uflash:w:/tmp/arduino_build_176337/robot_vector.ino.hex:i
Avrdude version 8.0-arduino.1
Copyright see https://github.com/avrdudes/avrdude/blob/main/AUTHORS
System wide configuration file is /home/dennis/.arduino15/packages/arduino/tools/avrdude/8.0.0-arduino1/etc/avrdude.conf
User configuration file /home/dennis/.avrduderc does not exist
Using port : /dev/ttyACM0
Using programmer : arduino
Setting baud rate : 115200
AVR part : ATmega328P
Programming modes : SPM, ISP, HVPP, debugWIRE
Programmer type : Arduino
Description : Arduino bootloader using STK500 v1 protocol
HW Version : 3
FW Version : 4.4
AVR device initialized and ready to accept instructions
Device signature = 1E 95 0F (ATmega328P, ATA6614Q, LGT8F328P)
Reading 21336 bytes for flash from input file robot_vector.ino.hex
in 1 section [0, 0x5357]: 167 pages and 40 pad bytes
Writing 21336 bytes to flash
Writing | ################################################## | 100% 3.44s
Reading | ################################################## | 100% 2.74s
21336 bytes of flash verified
Avrdude done. Thank you.
The above, seems to me, implies that I have enough memory, so I'm not sure what's going on.
Although I do understand about posting minimal code, in this case I don't know how to force the issue without sharing the entier code block. Please understand I am not looking for someone to debug the code; only for someone to explain why I cannot successfully include the print/println lines.
Thanks
#define DEBUGGING 1
// based largely on PicaioVideos post at https://github.com/Picaio/roboteyes/
// Features added for moving eyes up and down, automatic random eye movements + code cleanup, macro names etc.
#include <Adafruit_GFX.h>
#include <Adafruit_GrayOLED.h>
#include <Adafruit_SPITFT.h>
#include <Adafruit_SPITFT_Macros.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_SSD1306.h>
#include <gfxfont.h>
#include <splash.h>
#include <Wire.h>
#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)
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define BUTTON_LEFT 8 // Pin number for button 1
#define BUTTON_RIGHT 9 // Pin number for button 2
#define BUTTON_MOOD 10 // Pin number for button 3
#define BUTTON_UP 11 // Pin number for button 4
#define BUTTON_DOWN 12 // Pin number for button 5
#define PRESSED_LEFT 0x01
#define PRESSED_RIGHT 0x02
#define PRESSED_MOOD 0x04
#define PRESSED_UP 0x08
#define PRESSED_DOWN 0x10
#define MIN_EYE_X 0
#define MAX_EYE_X 31
#define MIN_EYE_Y 0
#define MAX_EYE_Y 31
#define MAX_WAIT_FOR_BUTTON 60000 // 60000 milliseconds = 60 seconds
#define MIN_EMULATE_BUTTONS_WAIT 1000 // 1000 milliseconds = 1 second
#define MIN_AUTO_MOOD_CHANGE_TIME 30000 // 30000 milliseconds = 30 seconds
#define MOOD1 0
#define MOOD_NORMAL 1
#define MOOD3 2
#define MOOD4 3
#define MOOD4 4
#define MOOD5 5
#define MAX_MOOD 5
#define DISPLAY_EYE 0
#define BLINK_EYE 1
#include "eyes.h"
unsigned long lastButtonPressTime ;
unsigned long lastButtonActionTime ;
unsigned long lastMoodChangeTime ;
int xp = 16;
int mood = 1;
void setup() {
Serial.begin(115200);
Serial.println("Booting");
pinMode(BUTTON_LEFT, INPUT_PULLUP); // Button 1
pinMode(BUTTON_RIGHT, INPUT_PULLUP); // Button 2
pinMode(BUTTON_MOOD, INPUT_PULLUP); // Button 3
pinMode(BUTTON_UP, INPUT_PULLUP); // Button 4
pinMode(BUTTON_DOWN, INPUT_PULLUP); // Button 5
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3c)) { // Address 0x3C for 128x64
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed; loop forever
}
// Show initial display buffer contents on the screen --
// the library initializes this with an Adafruit splash screen.
display.display();
// Clear the buffer
display.clearDisplay();
display.display();
lastButtonPressTime = millis() ;
lastButtonActionTime = millis() ;
lastMoodChangeTime = millis() ;
updateEyes(1) ; // Init only
}
unsigned char readButtons(void) {
unsigned char ret = 0; // No button pressed
if (digitalRead(BUTTON_LEFT) == 0) ret |= PRESSED_LEFT ;
if (digitalRead(BUTTON_RIGHT) == 0) ret |= PRESSED_RIGHT ;
if (digitalRead(BUTTON_MOOD) == 0) ret |= PRESSED_MOOD ;
if (digitalRead(BUTTON_UP) == 0) ret |= PRESSED_UP ;
if (digitalRead(BUTTON_DOWN) == 0) ret |= PRESSED_DOWN ;
if (ret != 0)
lastButtonPressTime = millis() ;
return (ret);
}
void loop() {
updateEyes(0) ;
}
void updateEyes(int init) {
int n ;
int buttonFlags ;
static int xd = 0;
static int delayTime = 0;
static int step = DISPLAY_EYE;
static int eyeLevel = 8 ;
static int lastX1, lastX2, lastY, lastMood ;
int x1, x2;
unsigned long epoch ;
int autoMood = 0 ;
int sinceLastButtonAction = epoch - lastButtonActionTime ;
int sinceLastMoodChange = epoch - lastMoodChangeTime ;
if (init) {
xd = 0 ;
delayTime = 0 ;
step = DISPLAY_EYE ;
eyeLevel = 8 ;
lastX1 = -1 ;
lastX2 = -1 ;
lastY = -1 ;
lastMood = -1 ;
return ;
}
if (delayTime > 0) { // was espera
delayTime-- ; // was espera
delay(1);
} else {
x1 = xd + (xp > 16 ? (16 + 2 * (xp - 16)) : xp);
x2 = 64 + xd + (xp < 16 ? (-16 + (xp * 2)) : xp);
switch (step) {
case DISPLAY_EYE:
display.clearDisplay(); // Clear the display buffer
if (x1 != lastX1 || x2 != lastX2 || eyeLevel != lastY || mood != lastMood) {
printStats(x1, x2, eyeLevel, mood) ;
lastX1 = x1 ;
lastX2 = x2 ;
lastY = eyeLevel ;
lastMood = mood ;
}
if (xp < 6) {
display.drawBitmap(x1, eyeLevel, peyes[mood][2][0], 32, 32, WHITE);
display.drawBitmap(x2, eyeLevel, peyes[mood][1][1], 32, 32, WHITE);
} else if (xp < 26) {
display.drawBitmap(x1, eyeLevel, peyes[mood][0][0], 32, 32, WHITE);
display.drawBitmap(x2, eyeLevel, peyes[mood][0][1], 32, 32, WHITE);
} else {
display.drawBitmap(x1, eyeLevel, peyes[mood][1][0], 32, 32, WHITE);
display.drawBitmap(x2, eyeLevel, peyes[mood][2][1], 32, 32, WHITE);
}
display.display();
delayTime = random(250, 1000);
n = random(0, 7);
if (n == 6) { // One time out of 7, set step to 1, else 2
step = BLINK_EYE ;
} else {
step = 2;
}
break;
case BLINK_EYE :
display.clearDisplay(); // Clear the display buffer
display.drawBitmap(x1, eyeLevel, eye0, 32, 32, WHITE);
display.drawBitmap(x2, eyeLevel, eye0, 32, 32, WHITE);
display.display();
delayTime = 100;
step = DISPLAY_EYE ;
break;
case 2: // TODO What's happening here?
n = random(0, 10);
if (n < 5) xd--;
if (n > 5) xd++;
if (xd < -4) xd = -3;
if (xd > 4) xd = 3;
delayTime = 0;
step = DISPLAY_EYE ;
break;
}
}
epoch = millis() ;
if ((( buttonFlags = readButtons()) == 0) // No button press
&& (sinceLastButtonAction >= MIN_EMULATE_BUTTONS_WAIT)) { // Maybe emulate a button press
n = random(0, 1000) ;
if (n < 100) { // one time out of 10
buttonFlags = PRESSED_LEFT ;
if (n < 33)
buttonFlags |= PRESSED_UP ;
if (n >= 66)
buttonFlags |= PRESSED_DOWN ;
} else if (n < 200) { // one time out of 10
buttonFlags = PRESSED_RIGHT ;
if (n < 133)
buttonFlags |= PRESSED_UP ;
if (n >= 166)
buttonFlags |= PRESSED_DOWN ;
} else if ((n > 970) && (sinceLastMoodChange >= MIN_AUTO_MOOD_CHANGE_TIME)) { // 3 times out of 100
buttonFlags = PRESSED_MOOD ;
autoMood = 1 ; // Mood change was automatic, not by button press
}
}
if (buttonFlags) { // If a button is pressed (or we're emulating same):
lastButtonActionTime = epoch ;
if (buttonFlags & PRESSED_UP) {
if (DEBUGGING) {
Serial.print("Pressed UP. Eye level is currently ") ;
Serial.print(eyeLevel) ;
}
eyeLevel = (eyeLevel <= MIN_EYE_Y) ? MIN_EYE_Y : eyeLevel - 1 ; // Higher on the display if lower Y
if (DEBUGGING) {
Serial.print("... and now it is ") ;
Serial.println(eyeLevel) ;
}
}
if (buttonFlags & PRESSED_DOWN) {
if (DEBUGGING) {
Serial.print("Pressed DOWN. Eye level is currently ") ;
Serial.print(eyeLevel) ;
}
eyeLevel = (eyeLevel >= MAX_EYE_Y) ? MAX_EYE_Y : eyeLevel + 1 ; // Lower on display if higher Y
if (DEBUGGING) {
Serial.print("... and now it is ") ;
Serial.println(eyeLevel) ;
}
}
if (buttonFlags & PRESSED_LEFT) {
xp = (xp <= 0 ? 0 : xp - 1); // Decrease x position unless = zero
}
if (buttonFlags & PRESSED_RIGHT) {
xp = (xp >= MAX_EYE_X ? MAX_EYE_X : xp + 1); // Increase X position unless = max
}
if (buttonFlags & PRESSED_MOOD) {
if (autoMood) { // Randomly select a mood, but skip MOOD3
while ((mood = random(0, MAX_MOOD)) == MOOD3) {} //select random until not equaL MOOD3
} else {
mood = (mood >= MAX_MOOD) ? 0 : mood + 1; // Between 0 and MAX_MOOD // TODO maybe mood = mod(mood+1, MAX_MOOD) ;
}
if (autoMood && mood == MOOD3) mood = MOOD4 ; // This is not a good automatically-chosen mood.
lastMoodChangeTime = epoch ;
}
delayTime = 0; // delayTime was espera
step = DISPLAY_EYE ;
// TODO There is a (VERY SLIM) chance that a button would be pressed between the time we decided to emulate a button-press and now.
// TODO If that happens, the following code will cause us to miss that button press. We could avoid by changing the while to
// TODO be sensitive to previous button-press value.
do {} while (readButtons() != 0) ; // Wait for button(s) to be released
}
}
void printStats(int x1, int x2, int eyeLevel, int mood) {
static int minX = 256, maxX = 0, minY = 256, maxY = 0 ;
char buffer[128] ;
minX = min(x1, min(x2, minX)) ;
maxX = max(x1, max(x2, maxX)) ;
minY = min(eyeLevel, minY) ;
maxY = max(eyeLevel, maxY) ;
sprintf(buffer, "X1=%2d X2=%2d Y=%2d Mood=%d Min X=%d, Max X=%d, Min Y=%d, max Y = %d", x1, x2, eyeLevel, mood, minX, maxX, minY, maxY) ;
Serial.println(buffer) ;
}