Arduino freezes after calling fingerprint sensor - memory leak? How to fix?

Hello all --

I am working on a project using the following setup:

Arduino Uno
Mini Thermal Printer from Adafruit
LCD Shield Kit 16x2 from Adafruit
Neopixel Stick (8x1) from Adafruit
Fingerprint Sesor - made by Kookye (Adafruit was all out of theirs).

According to the Kookye website, I have to use Arduino IDE 1.0.6, so that is what I am using. It throws up lots of errors w/ 1.0.8. The library for it looks almost identical to the library for the Adafruit fingerprint sensor, but is a separate one downloaded from the Kookye website - the .zip is found on the instructions page for this sensor. It also uses nearly identical (but distinct) fingeprint enrollment software (also found on the previous page).

I am having two (seemingly related) issues. First I will describe the code for the project, and then the issue I am having, and then I will include the code.

This is how it should work:
On the first row, the LCD display shows a 16 digit number in base-5 which increments with every iteration of loop().

Because 5^16 is a massive number, and it is supposed to go through every value eventually, I am doing this by writing a simple class, which stores a 16 digit int array.

There is an increment function which takes a a digits-place as input. It goes to that place, increments, checks if it is equal to the base, and if so, rolls back to zero and calls the incrementation function on the next place over.

The display just shows this number on the first row. In order to get decent speed, I do not update all 16 digits on the display every iteration of the loop, but rather, the incrementation function within the ticker class also updates the lcd with just the digit that has changed each time the incrementation is called on some digit of the 16-digit number.

The fingerprint scanner is used to unlock the d-pad/select buttons on the LCD Shield. For now, all I want to do is make it so that when an authorized user presses "select" with their finger on the scanner, the thermal printer then prints out the current state of the base-5 counter, and the second row of the LCD just stores that value as the first continues to increment.

The 8-pixel neopixel stick just displays a chromatic encoding of the first 8-digits of the base-5 counter (it is trivial to extend it to the appropriate 16 one everything else is working).

I am having two separate (but seemingly related) issues.

Using code which does not make reference to the fingerprint scanner or the neopixel, everything works fine - I simply press the button and the thermal printer outputs the appropriate number.

If I add in the code for the arduino to verify a user using the fingerprint scanner, what happens is -
I press the button with my finger.
It successfully authorizes.
The printer correctly outputs the number.
Messages that I then print to the Serial monitor or the LCD appear fine.
Upon exiting the if statement which is a call to the fingerprint authorization function, and which contains the "print the current state" code, the arduino resets.

If I use the Neopixel but not the fingerprint scanner, the neopixel updates fine as long as I do not try to print.
As soon as I try to print, the printer successfully prints, but then the arduino freezes.

If I try to use the Neopixel and the fingerprint scanner, the arduino freezes when I press the button for the printer to output the state.

My research seems to suggest that this is some sort of memory issue.

The output of freeram within the control statement is always between 900 and 1100 depending on which parts I have activated. It does DECREASE before the fingerprint scanner call and after.

Any help would be appreciated. Am I simply running out of RAM? I've looked at the classes for the fingerprint functions and the counter incrementation functions and the LCD and I'm pretty sure there are no chances of memory leaks, or using massive amounts of memory - well I could see the fingerprint scanner using a lot of memory, but it crashes many lines after the fingerprint scanner function is called - really only when exiting the if-statement that it controls... Also, I don't get why the code would crash using just the printer and lcd shield and a single 8x1 neopixel stick. It seems like using the button to thermal print the current state should not all of a sudden crash everything, as it using the button to control printing works perfectly without the neopixel code activated...

Anyways, all the code I've written is in the next post. I'm also using the Adafruit LCD Shield library (a modification of the LiquidCrystal library, also includes the Wire.h and MCP libraries), the Adafruit Neopixel Library, and the Adafruit Thermal Printer library.

Thank you in advance for any help you may be able to offer.

Attaching code.

cheyney_proj.ino (6.12 KB)

Ticker.cpp (1.82 KB)

Ticker.h (770 Bytes)

.ino :

//setup Fingerprint sensor includes and initialize sensor.
#include <peng_fei_Fingerprint.h>
#include <SoftwareSerial.h>

#define FINGER_RX_PIN 2  //IN from sensor (GREEN wire)
#define FINGER_TX_PIN 3  //OUT from arduino  (WHITE wire)
SoftwareSerial fingerSerial(FINGER_RX_PIN,FINGER_TX_PIN);
peng_fei_Fingerprint finger = peng_fei_Fingerprint(&fingerSerial);
int getFingerprintIDez();
boolean currentlyAuthorized=false;
int timeOfAuthorization;

// include code for neopixel library
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#define NEOPIN 8
#define PIXELS 8
// Parameter 1 = number of pixels in strip
// Parameter 2 = Arduino pin number (most are valid)
// Parameter 3 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
//   NEO_RGBW    Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXELS, NEOPIN, NEO_GRB + NEO_KHZ800);
uint32_t red = strip.Color(255, 0, 0);
uint32_t green = strip.Color(0,255,0);
uint32_t blue = strip.Color(0, 0, 255);
uint32_t yellow = strip.Color(255, 255,0);
uint32_t white = strip.Color(255, 255,255);



// include the library code for LCD Shield:
#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <utility/Adafruit_MCP23017.h>

// The shield uses the I2C SCL and SDA pins. On classic Arduinos
// this is Analog 4 and 5 so you can't use those for analogRead() anymore
// However, you can connect other I2C sensors to the I2C bus and share
// the I2C bus.
Adafruit_RGBLCDShield lcd = Adafruit_RGBLCDShield();

// These #defines make it easy to set the backlight color of LCD
#define RED 0x1
#define YELLOW 0x3
#define GREEN 0x2
#define TEAL 0x6
#define BLUE 0x4
#define VIOLET 0x5
#define WHITE 0x7




// include the library code for thermal printer and bitmap numbers
#include "Adafruit_Thermal.h"
/*#include "0bmp.h"
#include "1bmp.h"
#include "2bmp.h"
#include "3bmp.h"
#include "4bmp.h" */
// Define pins for printer USE, set up a serial connection
#define PRINTER_TX_PIN 6 // Arduino transmit to printer YELLOW WIRE  labeled RX on printer
#define PRINTER_RX_PIN 7 // Arduino receive from printer  GREEN WIRE   labeled TX on printer
SoftwareSerial printerSerial(PRINTER_RX_PIN, PRINTER_TX_PIN); // Declare SoftwareSerial obj first
Adafruit_Thermal printer(&printerSerial); 




    

// Include class for base-5 clock ticker, and declare
#include <Ticker.h>
Ticker ticker(5,16);

/*unsigned long time1;
unsigned long time2;*/


void setup() {
  
  Serial.begin(9600);

  initializeLCD();

  initializePrinter(); 

  initializeFinger();

  
  initializePixels();
  strip.setBrightness(15);
}

void loop() {
  
  
  // time1=micros();
  ticker.incrementTicker(lcd,strip);
  // time2=micros();
  // Serial.println(time2-time1); // check duration of ticker incrementation - 48us without lcd: 768 cpu cycles, 11ms with lcd: 176,000 cpu cycles.

  //delayMicroseconds(24);
 

  uint8_t buttons = lcd.readButtons();
  if (buttons && buttons == BUTTON_SELECT) { 
    Serial.print(F("Free RAM (before calling fingerprint): "));
    Serial.println(freeRam());
    if (getFingerprintIDez()>0) {
      
      Serial.print(F("Free RAM (after calling fingerprint): "));
      Serial.println(freeRam());
      
      lcd.setCursor(0,1);
      lcd.print(F("Authorized!      "));
      delay(1000);
      
      registerPositionAndPrint();
      delay(1000);
      
      Serial.print(F("Free RAM (before exiting IF): "));
      Serial.println(freeRam());
      
    } else {
      lcd.setCursor(0,1);
      lcd.print(F("Not Authorized!"));
      delay(1000);
    }
  } 
}


void registerPositionAndPrint() {
  //String currentPosition = ticker.currentPosition();
  printer.wake();
  printer.justify('C');
  for (int i = sizeof(ticker.ticker)/sizeof(int)-1; i>=0; i--) {
    printer.print(ticker.ticker[i]);
  }
  printer.feed(5);
  printer.sleep();

  lcd.setCursor(0,1);
  lcd.print(ticker.getPosition());
}

void initializePrinter() {
  // NOTE: SOME PRINTERS NEED 9600 BAUD instead of 19200, check test page.
  printerSerial.begin(19200);  // Initialize SoftwareSerial
  //Serial1.begin(19200); // Use this instead if using hardware serial
  printer.begin();        // Init printer (same regardless of serial type)
  /*printer.feed(2);
  printer.println(F("Printer has initialized"));
  printer.feed(2);*/
  printer.sleep();
}

void initializeLCD() {
  // set up the LCD's number of columns and rows, as well as backlight color: 
  lcd.begin(16, 2);
  lcd.setBacklight(WHITE);
  lcd.setCursor(0,0);
  lcd.print("0000000000000000");
}

void initializeFinger() {
  lcd.setCursor(0,1);
  lcd.print(F("Thumb Sensor?"));
  Serial.println(F("Looking for thumb sensor..."));
  delay(1000);
  // set the data rate for the sensor serial port
  finger.begin(57600);
  
  if (finger.verifyPassword()) {
    lcd.setCursor(0,1);
    lcd.print("                         ");
    lcd.setCursor(0,1);
    lcd.print(F("Found sensor!"));
    Serial.println(F("Found Sensor!"));
    delay(1000);
  } else {
    lcd.setCursor(0,1);
    lcd.print(F("Error: No sensor   "));
    Serial.println(F("Error: No sensor found."));
    delay(3000);
  }
}

int getFingerprintIDez() {
  uint8_t p;
 
  p = finger.getImage();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)  return -1;

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  return -1;
  
  return finger.fingerID; 
}

void initializePixels() {
// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
  #if defined (__AVR_ATtiny85__)
    if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
  #endif
  // End of trinket special code


  for (int i = 0; i<strip.numPixels();i++) {
    strip.begin();

    strip.setPixelColor(i,0,0,0);
    strip.show();
  }
} 

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Ticker.h:

/*
  Ticker.h - Library for base-5 clock.
  Created by Sam Wolk, 2017.
  
*/

#ifndef Ticker_h
#define Ticker_h

#include "Arduino.h"
#include <Wire.h>
#include <Adafruit_RGBLCDShield.h>
#include <utility/Adafruit_MCP23017.h>
#include <Adafruit_NeoPixel.h>


class Ticker
{
  public:
    Ticker(int base_, int numberOfPlaces_);
    int ticker[16];
    void incrementTicker(Adafruit_RGBLCDShield screen);
 void incrementTicker(Adafruit_RGBLCDShield screen,Adafruit_NeoPixel strip);

    void incrementTicker(int placeToStart,Adafruit_RGBLCDShield lcd);
 void incrementTicker(int placeToStart,Adafruit_RGBLCDShield lcd,Adafruit_NeoPixel strip);

    String getPosition();
    int _numberOfPlaces;
    int _base;
  private:
    
};

#endif

Ticker.cpp :

/*
  Ticker.h - Library for base-5 clock.
  Created by Sam Wolk, 2017.
  
*/




#include "Arduino.h"

#include "Ticker.h"


Ticker::Ticker(int base_, int numberOfPlaces) {
  int ticker[numberOfPlaces];
  _base = base_;
  _numberOfPlaces=numberOfPlaces;
}

void Ticker::incrementTicker(Adafruit_RGBLCDShield screen) {
  incrementTicker(0,screen);
}

void Ticker::incrementTicker(Adafruit_RGBLCDShield screen,Adafruit_NeoPixel strip) {
  incrementTicker(0,screen,strip);
}

void Ticker::incrementTicker(int placeToStart,Adafruit_RGBLCDShield lcd) {
  ticker[placeToStart]=(ticker[placeToStart]+1) % _base;
  lcd.setCursor(15-placeToStart,0);
  lcd.print(ticker[placeToStart]);
  if (ticker[placeToStart]==0) incrementTicker((placeToStart+1)%_numberOfPlaces,lcd); 
}

void Ticker::incrementTicker(int placeToStart,Adafruit_RGBLCDShield lcd,Adafruit_NeoPixel neopixel) {
  ticker[placeToStart]=(ticker[placeToStart]+1) % _base;
  lcd.setCursor(15-placeToStart,0);
  lcd.print(ticker[placeToStart]);
  
  neopixel.begin();
  neopixel.setPixelColor(0,0,0,0);
  if (ticker[placeToStart]==0) neopixel.setPixelColor(7-placeToStart, 0,0,0);
  if (ticker[placeToStart]==1) neopixel.setPixelColor(7-placeToStart, 0,0,255);
  if (ticker[placeToStart]==2) neopixel.setPixelColor(7-placeToStart, 255,0,0);
  if (ticker[placeToStart]==3) neopixel.setPixelColor(7-placeToStart, 255,255,0);
  if (ticker[placeToStart]==4) neopixel.setPixelColor(7-placeToStart, 255,255,255);
  neopixel.show();
  if (ticker[placeToStart]==0) incrementTicker((placeToStart+1)%_numberOfPlaces,lcd,neopixel); 
}

String Ticker::getPosition() {
  String justRegisteredPosition;
  for (int i = 0; i<sizeof(ticker)/sizeof(int); i++) {
    justRegisteredPosition= ticker[i]+justRegisteredPosition;
  }
  return justRegisteredPosition;
}

My research seems to suggest that this is some sort of memory issue.

Quite possible.

The Ticker class has a getPosition() method that uselessly pisses away resources by returning a String. Why? You need to avoid Strings like the plague.