如何减少Arduino Uno的内存使用量

时间:2016-07-01 00:10:55

标签: memory arduino rom

我正在使用Arduino UNO,Dccduino的克隆,我的内存有问题.Sketch使用25,114字节(77%)的程序存储空间。最大值为32,256字节。全局变量使用1,968字节(96%)的动态内存,为本地变量留下80字节。最大值为2,048字节。内存不足,可能会出现稳定性问题。 有没有办法减少20%的内存,如果不是我认为我必须购买Arduino Mega

以下是代码:

#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClib.h"
#include <GPRS_Shield_Arduino.h>
#include <SoftwareSerial.h>

// Data wire is plugged into port 3 and 2 on the Arduino
#define ONE_WIRE_BUS_1 3  // Many sensors on pin 3 
#define ONE_WIRE_BUS_2 2 // Many sensors on pin 2
#define TEMPERATURE_PRECISION 9 // Lower resolution
#define PIN_TX    7
#define PIN_RX    8
#define BAUDRATE  9600
#define PHONE_NUMBER  "xxxxxxxxxxxxx"

GPRS gprsTest(PIN_TX, PIN_RX, BAUDRATE); //RX,TX,PWR,BaudRate

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire1(ONE_WIRE_BUS_1);
OneWire oneWire2(ONE_WIRE_BUS_2);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors1(&oneWire1);
DallasTemperature sensors2(&oneWire2);
int numberOfDevices1; // Number of temperature devices found on pin 3
int numberOfDevices2; // Number of temperature devices found on pin 2
DeviceAddress tempDeviceAddress1; // We'll use this variable to store a found device address for bus 3
DeviceAddress tempDeviceAddress2; // We'll use this variable to store a found device address for bus 2

File myFile;

RTC_DS3231 rtc; // Create a RealTimeClock object


void setup(void)
{

  // start serial port
#ifndef ESP8266
  while (!Serial); // for Leonardo/Micro/Zero
#endif
  Serial.begin(9600);
  delay(3000);
  Serial.println(F("Dallas Temperature IC Control Library Demo"));


  Serial.print( F("Initializing SD card..."));

  if (!SD.begin(4)) {
    Serial.println(F("\ninitialization failed!"));
    return;
  }
  Serial.println(F("initialization done."));

  if (! rtc.begin()) {
    Serial.println("Couldn't find RTC");
    while (1);
  }


  if (rtc.lostPower()) {
    Serial.println("RTC lost power, lets set the time!");
    // following line sets the RTC to the date & time this sketch was compiled
    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

   /* while(!gprsTest.init()) { //gprs init
      delay(1000);
      Serial.print(F("init error\r\n")); 
      Serial.println(F("gprs init success"));*/ It takes 20% of dynamic memory so i cant use it
  }
  Serial.println(F("start to call ..."));// Call when device will start
  gprsTest.callUp(PHONE_NUMBER);
  Serial.println("start to send message ...");
  gprsTest.sendSMS(PHONE_NUMBER, "Hi device is ON"); //define phone number and text


  // Start up the library
  sensors1.begin();
  sensors2.begin();

  // Grab a count of devices on the wire
  numberOfDevices1 = sensors1.getDeviceCount();
  numberOfDevices2 = sensors2.getDeviceCount();

  // locate devices on the bus
  Serial.print(F("Locating devices..."));

  Serial.print(F("Found "));
  Serial.print(numberOfDevices1, DEC );
  Serial.print(F("+"));
  Serial.print(numberOfDevices2, DEC );
  Serial.println(F(" devices."));

  // report parasite power requirements
  Serial.print("Parasite power is: ");
  if (sensors1.isParasitePowerMode()) Serial.println(F("Sensors 1 ON"));
  else Serial.println(F("\nSensors 1 OFF"));
  if (sensors2.isParasitePowerMode()) Serial.println(F("Sensors 2 ON"));
  else Serial.println(F("Sensors 2 OFF"));


  // Loop through each device, print out address for pin 3
  for (int i = 0; i < numberOfDevices1; i++)
  {
    // Search the wire for address
    if (sensors1.getAddress(tempDeviceAddress1, i))
    {
      Serial.print(F("Found device "));
      Serial.print(i, DEC);
      Serial.print(F(" with address: "));
      printAddress(tempDeviceAddress1);
      Serial.println();
      Serial.println(F("\n"));
      // set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
      sensors1.setResolution(tempDeviceAddress1, TEMPERATURE_PRECISION);


    } else {
      Serial.print(F("Found ghost device for pin 3 at "));
      Serial.print(i, DEC);
      Serial.print(F(" but could not detect address. Check power and cabling"));
    }
  }


  // Loop through each device, print out address for pin 2
  for (int i = 0; i < numberOfDevices2; i++)
  {
    // Search the wire for address
    if (sensors2.getAddress(tempDeviceAddress2, i))
    {
      Serial.print(F("Found device "));
      Serial.print(i + numberOfDevices1, DEC);
      Serial.print(F(" with address: "));
      printAddress(tempDeviceAddress2);
      Serial.println();
      Serial.println(F("\n"));
      // set the resolution to TEMPERATURE_PRECISION bit (Each Dallas/Maxim device is capable of several different resolutions)
      sensors2.setResolution(tempDeviceAddress2, TEMPERATURE_PRECISION);

    } else {
      Serial.print(F("Found ghost device for pin 2 at "));
      Serial.print(i, DEC);
      Serial.print(F(" but could not detect address. Check power and cabling"));
    }
  }

}



void loop(void)
{
  // call sensors1.requestTemperatures() to issue a global temperature
  // request to all devices on the bus
  Serial.print(F("Requesting temperatures to pin 3..."));
  sensors1.requestTemperatures(); // Send the command to get temperatures for pin 3
  Serial.println(F("DONE"));

  myFile = SD.open("test1.txt", FILE_WRITE); //open file

  // Loop through each device , print out temperature data for pin 3
  for (int i = 0; i < numberOfDevices1; i++)
  {
    // Search the wire for address
    if (sensors1.getAddress(tempDeviceAddress1, i))
    {
      // Output the device ID
      Serial.print(F("Temperature for device: "));
      Serial.println(i, DEC);

      // It responds almost immediately. Let's print out the data

      printTemperature1(tempDeviceAddress1);// Use a simple function to print out the data

      Serial.print(F("\n"));
    }
    delay(4000);
    //else ghost device! Check your power requirements and cabling
  }// End forloop for pin 3
  if (numberOfDevices2 != 0) {
    Serial.print(F("Requesting temperatures to pin 2..."));
    sensors2.requestTemperatures(); // Send the command to get temperatures for pin 2
    Serial.println(F("DONE"));
  }


  // Loop through each device for pin 2, print out temperature data
  for (int i = 0; i < numberOfDevices2; i++)
  {
    // Search the wire for address
    if (sensors2.getAddress(tempDeviceAddress2, i))
    {
      // Output the device ID
      Serial.print(F("Temperature for device: "));
      Serial.println(i + numberOfDevices1, DEC);
      // It responds almost immediately. Let's print out the data
      printTemperature2(tempDeviceAddress2);// Use a simple function to print out the data
      Serial.print(F("\n"));
    }
    else Serial.print(F("ghost device! Check your power requirements and cabling"));
    delay(4000);
  } //End forloop for pin 3

  myFile.close(); // Should I close it?

}// End loop()






void printAddress(DeviceAddress deviceAddress) // function to print a device address
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (deviceAddress[i] < 16) Serial.print(F("0"));
    Serial.print(deviceAddress[i], HEX);
  }
}






void printTemperature1(DeviceAddress deviceAddress1) // function to print the temperature for a device  (pin 3)
{
  float tempC = sensors1.getTempC(deviceAddress1);
  Serial.print("Temp C: ");
  Serial.print(tempC);
  if (myFile)
  {
    Serial.println(F("\nWriting to test.txt..."));
    myFile.print(F("C: "));
    myFile.print(tempC);
    print_time(); // Call print_time() function to print time on file
    myFile.print(F("\n"));
    Serial.print(F("Done!"));
  }
  else Serial.print(F("Error opening file 1"));
  Serial.println("\n");
}



void printTemperature2(DeviceAddress deviceAddress2) // function to print the temperature for a device (pin 2)
{
  float tempC = sensors2.getTempC(deviceAddress2);
  Serial.print(F("Temp C: "));
  Serial.print(tempC);
  if (myFile)
  {
    Serial.print(F("\nWriting to test.txt..."));
    myFile.print(F("C: "));
    myFile.print(tempC);
    print_time(); // Call print_time() function to print time on file
    myFile.print(F("\n"));
    Serial.print(F("Done!"));
  } else Serial.print(F("Error opening file 2"));

  Serial.println("\n");
}

void print_time() { // print time function

  DateTime now = rtc.now();
  Serial.print(now.year(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.day(), DEC);
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();
  myFile.print(now.year(), DEC);
  myFile.print('/');
  myFile.print(now.month(), DEC);
  myFile.print('/');
  myFile.print(now.day(), DEC);
  myFile.print(now.hour(), DEC);
  myFile.print(':');
  myFile.print(now.minute(), DEC);
  myFile.print(':');
  myFile.print(now.second(), DEC);
  myFile.println();
}

3 个答案:

答案 0 :(得分:1)

我想我之前在另一个网站上看过这段代码。 从您的代码中我假设您正在制作温度记录器,并且您希望将数据记录在SD卡上。 如果使用DS1307RTC.h库和Time.h库,则大多数代码都是冗余的。 DS1307RTC是一个通用的RTC库。有了它,你不需要OneWire,Wire或SPI和Wire。软件序列也是不必要的。 但是,我建议你在Github上查看我的Arduino DataLogger库:https://github.com/FreelanceJavaDev/DataLogger

我将我在Uno上保存的内存大大减少到22,680字节(70%)的程序存储空间和1,237字节(60%)的SRAM(动态内存)。它会自动配置RTC和SD卡。它根据日期组织运行的每个月生成一个CSV文件以便导出为ex​​cel。

答案 1 :(得分:1)

我已经在我的时间内构建了几个Arduino数据记录器,并且所有使用SD卡的记录器由于内存不足而失败。 SD本身使用了ATMega 328上可用内存的一半。为其他硬件添加了几个库,你的草图根本就没有内存。

我已经转到了24LC512s。一个通常就足够了,但如果你愿意,最多可以使用4个不同的地址。这是一个相对少量的内存,但我发现它总是足够的。生成数兆字节的数据太容易了,而这些数据太大而无法分析。一个24LC512为一个电子表格保存了足够的数据。唯一的缺点是你必须使用Arduino通过USB读回数据。

我使用前两个字节来存储记录数,并使用下一个字节来存储每条记录的字节数。 (虽然回想起后者并不是必需的。)你可能会认为前两个字节会“磨损”,因为每次创建一个新条目时它们都会被重写,但这还没有发生在我身上。我已经有相同的Arduino连续运行了7年(除了草图的一些更新之外不间断),并且每月生成超过1000条记录,因此前两个字节必须已经更新了这么多次。我从来没有遇到前两个字节的问题,即使我这样做,它也足够便宜来取代24LC512。

你甚至可以通过“热插拔”逃脱:我让记录器运行并更换内存芯片以换取新的记录器,这样我就可以在不中断记录器的情况下读取数据。 (读取记录数,然后递增并写入新数字和数据。)

答案 2 :(得分:1)

对于我的谷仓中的系统,我要做的事情不止于此-SD卡,RTC,LCD显示器,GPRS调制解调器,与其他设备的无线电通信,根据季节编程控制泵,降雨传感器,浮子传感器,温度传感器,电压传感器等。这是我发现的一些东西:

  • 所有字符串文字应替换为F()宏调用或Flash上​​的本机字符串函数。因此,strcpy_P(string1,PSTR(string2)),strcat_P(string1,PSTR(string2))这种调用。
  • 将对设备的大量访问转换为函数调用,这些函数调用包含那些调用所需的任何数据结构。您的arduino将更加努力地添加和删除堆栈以及堆栈帧,但是完成该功能后,数据结构将从堆栈中删除,并且Arduino的机器周期比RAM便宜得多。因此,将温度读取代码与文件写入功能分开。返回浮点数,然后将该浮点数发送到您的SD编写函数。
  • 将所有串行代码隐藏在debug_print代码中。因此,您将在调试时使用debug_print调用,它们都将从生产代码中完全消失。
  • 确保仅在子例程中调用SD代码,并根据需要而不是在全局范围内实例化这些子例程中的实际FAT代码。
  • 有许多不同的SD库,其中一些比其他库便宜(在内存方面)。货比三家。
  • 在调用那些需要大量RAM的SD调用之一之前,使用freeMemory变体之一来确定您是否有可用的内存。如果您当时没有足够的RAM,则可能需要在EEPROM中实现某种循环缓冲区,以便在确实有可用RAM时存储要写入SD的消息。
  • 在可能的情况下,使用布尔值和字节而不是整数,并考虑对标志使用位字段以节省更多的RAM。您真的要在温度总线上安装多达32,000个设备吗?您可以在一个字节中得到255。