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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何减少Arduino Uno的内存使用量相关的知识,希望对你有一定的参考价值。

我正在使用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("
initialization 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
")); 
      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("
Sensors 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("
"));
      // 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("
"));
      // 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("
"));
    }
    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("
"));
    }
    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("
Writing to test.txt..."));
    myFile.print(F("C: "));
    myFile.print(tempC);
    print_time(); // Call print_time() function to print time on file
    myFile.print(F("
"));
    Serial.print(F("Done!"));
  }
  else Serial.print(F("Error opening file 1"));
  Serial.println("
");
}



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("
Writing to test.txt..."));
    myFile.print(F("C: "));
    myFile.print(tempC);
    print_time(); // Call print_time() function to print time on file
    myFile.print(F("
"));
    Serial.print(F("Done!"));
  } else Serial.print(F("Error opening file 2"));

  Serial.println("
");
}

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();
}
答案

我想我之前在其他网站上看过这段代码。从您的代码中我假设您正在制作温度记录器,并且您希望将数据记录在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。

另一答案

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

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

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

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

另一答案

我正在为谷仓中的系统做更多的事情 - 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。

以上是关于如何减少Arduino Uno的内存使用量的主要内容,如果未能解决你的问题,请参考以下文章

Arduino UNO与树莓派3B搭建一个视觉小车

如何给Arduino UNO下载Bootloader?

arduino uno 如何对模拟口得到的数据进行处理得到平滑的曲线,用到相关的库也行,想要详细的程序。

如何在我的 Arduino Uno (Mac OSX) 上保持串行地址一致?

使用单电机控制 Arduino (Uno) 库控制两个步进电机

为啥arduino uno只能保留两位小数