In my quest to understand memory usage and lower it as much as possible, I ran avr-nm -Crtd --size-sort to see what portions of the code are the most taxing on the RAM.
The problem:
Memory is being allocated to irrelevant things (e.x. digitalwrite using 152 bytes even though I do not use it at all in my sketch) and a bunch of memory is being used by Serial this and Serial that for a reason I do not understand.
What is going on here???
How can I interpret these results???
Is there a method to trace these large memory blocks even further?
For reference if needed:
The binutil output:
00001272 T main
00000574 T __vector_24
00000480 t TwoWire::endTransmission(unsigned char) [clone .constprop.23]
00000414 t twi_readFrom.constprop.15
00000410 t I2Cdev::readBytes(unsigned char, unsigned char, unsigned char, unsigned char*, unsigned int, void*)
00000178 t I2Cdev::writeBits(unsigned char, unsigned char, unsigned char, unsigned char, unsigned char, void*)
00000157 b Serial
00000154 t HardwareSerial::write(unsigned char)
00000152 t digitalWrite
00000148 T __vector_16
00000142 t global constructors keyed to 65535_0_verywildforthenight.ino.cpp.o.2679
00000112 t twi_stop
00000100 T __vector_18
00000094 t TwoWire::write(unsigned char)
00000094 t I2Cdev::writeBytes(unsigned char, unsigned char, unsigned char, unsigned char*, void*) [clone .constprop.17]
00000094 t writeByte(unsigned char, unsigned char, unsigned char) [clone .constprop.18]
00000090 t TwoWire::write(unsigned char const*, unsigned int)
00000090 t Print::write(unsigned char const*, unsigned int)
00000078 t twi_transmit
00000076 T __vector_19
00000074 t micros
00000068 t HardwareSerial::_tx_udr_empty_irq()
00000068 T __udivmodsi4
00000066 t twi_handleTimeout
00000064 t HardwareSerial::flush()
00000060 t twi_init
00000046 T __vector_6
00000046 T __divmodsi4
00000042 t WDT_INIT()
00000040 t HardwareSerial::read()
00000038 t TwoWire::read()
00000033 b mpu
00000032 b twi_txBuffer
00000032 b twi_rxBuffer
00000032 b twi_masterBuffer
00000032 b TwoWire::txBuffer
00000032 b TwoWire::rxBuffer
00000030 t TwoWire::peek()
00000030 t HardwareSerial::availableForWrite()
00000030 T __umulhisi3
00000028 t HardwareSerial::peek()
00000024 t millis
00000024 t HardwareSerial::available()
00000022 T __muluhisi3
00000022 T __do_global_ctors
00000022 T __do_copy_data
00000020 t digital_pin_to_timer_PGM
00000020 t digital_pin_to_port_PGM
00000020 t digital_pin_to_bit_mask_PGM
00000020 t Serial0_available()
00000020 t serialEventRun()
00000020 T __vector_1
00000018 d vtable for TwoWire
The code
#include <avr/wdt.h>
#include "Wire.h"
#include "I2Cdev.h"
#include "MPU6050.h"
#include <avr/sleep.h>
#define SIGNAL_PATH_RESET 0x68
#define INT_PIN_CFG 0x37
#define ACCEL_CONFIG 0x1C
#define MOT_THR 0x1F // Motion detection threshold bits [7:0]
#define MOT_DUR 0x20 // Duration counter threshold for motion interrupt generation, 1 kHz rate, LSB = 1 ms
#define MOT_DETECT_CTRL 0x69
#define INT_ENABLE 0x38
#define WHO_AM_I_MPU6050 0x75 // Should return 0x68
#define INT_STATUS 0x3A
#define ADO 0
#if ADO
#define MPU6050_ADDRESS 0x69 // Device address when ADO = 1
#else
#define MPU6050_ADDRESS 0x68 // Device address when ADO = 0
#endif
MPU6050 mpu;
int16_t ax, ay, az;
int16_t gx, gy, gz;
struct MyData {
byte X;
byte Y;
byte Z;
};
MyData data;
enum state_t {
START,
IN_WARNING_1,
IN_WARNING_2,
SEAT_BELT_OK,
SEAT_BELT_NOK_ACCEPTED,
} ;
state_t state = state_t::SEAT_BELT_OK;
uint32_t inCurrentStateAtMs ;
const uint8_t wakePin = 2; // pin used for waking up
const uint8_t seatPin = 5 ; // active low
const uint8_t buzzer = 4;
void setState( state_t newState ) {
state = newState ;
inCurrentStateAtMs = millis() ;
}
void writeByte(uint8_t address, uint8_t subAddress, uint8_t data) {
Wire.begin();
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.write(data); // Put data in Tx buffer
Wire.endTransmission(); // Send the Tx buffer
}
uint8_t readByte(uint8_t address, uint8_t subAddress) {
uint8_t data; // `data` will store the register data
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.endTransmission(false); // Send the Tx buffer, but send a restart to keep connection alive
Wire.requestFrom(address, (uint8_t) 1); // Read one byte from slave register address
data = Wire.read(); // Fill Rx buffer with result
return data; // Return data read from slave register
}
void setup() {
wdt_disable ();
Wire.begin();
mpu.initialize();
writeByte(MPU6050_ADDRESS, 0x6B, 0x00);
writeByte(MPU6050_ADDRESS, SIGNAL_PATH_RESET, 0x07); //Reset all internal signal paths in the MPU-6050 by writing 0x07 to register 0x68;
writeByte(MPU6050_ADDRESS, ACCEL_CONFIG, 0x01); //Write register 28 (==0x1C) to set the Digital High Pass Filter, bits 3:0. For example set it to 0x01 for 5Hz. (These 3 bits are grey in the data sheet, but they are used! Leaving them 0 means the filter always outputs 0.)
writeByte(MPU6050_ADDRESS, MOT_THR, 10); //Write the desired Motion threshold to register 0x1F (For example, write decimal 20).
writeByte(MPU6050_ADDRESS, MOT_DUR, 40); //Set motion detect duration to 1 ms; LSB is 1 ms @ 1 kHz rate
writeByte(MPU6050_ADDRESS, MOT_DETECT_CTRL, 0x15); //to register 0x69, write the motion detection decrement and a few other settings (for example write 0x15 to set both free-fall and motion decrements to 1 and accelerometer start-up delay to 5ms total by adding 1ms. )
writeByte(MPU6050_ADDRESS, INT_ENABLE, 0x40); //write register 0x38, bit 6 (0x40), to enable motion detection interrupt.
writeByte(MPU6050_ADDRESS, INT_PIN_CFG, 160); // now INT pin is active low
//pinMode( seatPin, INPUT_PULLUP ) ;
//pinMode(wakePin, INPUT_PULLUP); // wakePin is pin no. 2
//pinMode(buzzer, OUTPUT);
//these do not include the led since it will be deleted evenutally
//unsure of how to classify pins used by MPU
DDRD = B11011011; //defaults at output to save power. buzzer is output
PORTD = B00100100; //enable the pull up resistors
setState( state_t::START ) ;
}
//creating alternative to attachinterrupt
// EICRA = 0; //low generates interrupt
//EIMSK |= (1<<INT0); //extenal pin interrupt is enabled
uint16_t readdata;
void Sleep_CPU(void){
SMCR |= (1 << 2); //power down mode
SMCR |= 1;//enable sleep
EIMSK |= (1<<INT0); //extenal pin interrupt is enabled
__asm__ __volatile__("sleep");//in line assembler to go to sleep
SMCR = 0;
EIMSK &= ~(1<<INT0); //extenal pin interrupt is enabled
readdata = readByte(MPU6050_ADDRESS, INT_STATUS);
//I think I should be detach interrupts to some capacity here
}
void WDT_INIT (void){
cli();
MCUSR &= ~(1<<WDRF); //clearing the WD reset flag.
WDTCSR = (1<<WDCE) | (1<<WDE);
WDTCSR = (1<<WDIE ) | (1<< WDP2 ) | (1<<WDP0 ); // set WDIE ( Interrupt only, no Reset ) and 1 second TimeOut
wdt_reset();
//digitalWrite(LED_BUILTIN, HIGH); // turn off LED to show sleep mode
SMCR |= (1 << 2); //power down mode
SMCR |= 1;//enable sleep
sei();
__asm__ __volatile__("sleep");//in line assembler to go to sleep
SMCR = 0;
//digitalWrite(LED_BUILTIN, LOW); // turn off LED to show sleep mode
//I think I should be detach interrupts to some capacity here
}
ISR (INT0_vect) {
}
ISR(WDT_vect) {
wdt_disable ();
}
void loop() {
static boolean outputTone = false;
static bool movementInCurrentWindow = false ;
static bool carDeemedStopped = true ;
static uint32_t lastMovAtMs = millis() ;
uint32_t ms = millis() ;
static uint8_t xlast = 0; //tradeoff dynamic memory vs program memory. this is better for program space but increase global variables by 2 bytes
static uint8_t ylast = 0;
static uint8_t zlast = 0;
xlast = data.X;
ylast = data.Y;
zlast = data.Z;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
data.X = map(ax, -17000, 17000, 0, 255 ); // X axis data
data.Y = map(ay, -17000, 17000, 0, 255);
data.Z = map(az, -17000, 17000, 0, 255); // Y axis data
if((abs(xlast) - abs(data.X)) > 3 || (abs(ylast) - abs(data.Y)) > 3){
movementInCurrentWindow = true;
carDeemedStopped =false;
lastMovAtMs = ms;
}
if(abs(xlast - data.X)<= 3 || abs(ylast - data.Y) <= 3){
movementInCurrentWindow = false;
}
if ( ms - lastMovAtMs > 1000 ) {
carDeemedStopped = true ;
movementInCurrentWindow = false ;
}
if ( (((PIND & (1 << seatPin)) >> seatPin) == 0) && state != state_t::SEAT_BELT_OK && state != state_t::START) {
setState( state_t::SEAT_BELT_OK ) ;
}
switch ( state ) {
case state_t::START : {
for (int i = 0; i<3 ; i++){
Sleep_CPU();
}
if ( movementInCurrentWindow ) {
setState( state_t::IN_WARNING_1 ) ;
movementInCurrentWindow = false ;
}
break ;
}
case state_t::IN_WARNING_1 : {
for( int i = 0; i<5; i++){
PORTD |= (1<<buzzer);
WDT_INIT();
PORTD &= ~(1<<buzzer);
for (int i = 0; i<8 ; i++){
WDT_INIT();
}
}
setState( state_t::IN_WARNING_2 ) ;
PORTD &= ~(1<<buzzer);
break ;
}
case state_t::IN_WARNING_2 : {
for( int i = 0; i<20; i++){
PORTD |= (1<<buzzer);
WDT_INIT();
PORTD &= ~(1<<buzzer);
WDT_INIT();
}
PORTD &= ~(1<<buzzer);
setState( state_t::SEAT_BELT_NOK_ACCEPTED ) ;
break ;
}
case state_t::SEAT_BELT_OK : {
if ((((PIND & (1 << seatPin)) >> seatPin) == 1) ) {
setState( state_t::START) ;
}
if ( carDeemedStopped ) {
setState( state_t::START ) ;
}
for (int i = 0; i<16 ; i++){
WDT_INIT();
}
break ;
}
case state_t::SEAT_BELT_NOK_ACCEPTED : {
if ( carDeemedStopped ) {
setState( state_t::START ) ;
}
for (int i = 0; i<16 ; i++){
WDT_INIT();
}
break ;
}
} // switch
} // loop