Подстрока: Что вызывает усечение вывода?
Подозреваю, что использую слишком много ресурсов из-за строк, но я новичок в Arduino, поэтому не уверен, где я ошибаюсь. Любая помощь будет оценена по достоинству.
Что он должен делать по завершении: "Напоминание о дне рождения"
- Прочитайте SD-карту для файла "день рождения" ("MM-DD.txt"), что соответствует "сегодня".
- Файл содержит имя человека и год рождения.
- Мигание светодиодов и отображение имени и возраста человека на OLED.
- Устройство не будет отображаться до чьего-либо дня рождения.
Я использую SD-карту с простым форматом, чтобы легко добавлять новые дни рождения.
Output:
⸮Initializing SD card...
card initialized.
25
111
opening: 04-25.txt
2020"Test Person"
bYearStr: 20
Файл SD-карты:
Name: MM-DD.txt
Example: 04-25.txt
Contents: Birthyear, Name
Example: 2020"Test Person"
Скетч
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "RTClib.h"
RTC_DS3231 rtc;
int age;
int led;
int dly;
String bName;
String bYearStr;
int bYear;
int currentDay;
int tDy;
int tMth;
int tYr;
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void setup() {
Serial.begin(9600);
delay(3000); // wait for console opening
// Setup Candle pins
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
pinMode(5, OUTPUT);
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));
}
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display(); // this command will display all the data which is in buffer
display.setTextColor(WHITE, BLACK);
const int SdCardPin = 10;
Serial.println("Initializing SD card..."); // Verifying SD Card.
// if (!SD.begin(SdCardPin)) {
if (!SD.begin(10)) {
Serial.println("Card failed, or not present"); // don't do anything more:
while (1);
}
Serial.println("card initialized.");
currentDay = 111;
}
void loop() {
age = 0;
DateTime now = rtc.now();
tDy = now.day();
tMth = now.month();
tYr = now.year();
Serial.println(tDy);
Serial.println(currentDay);
if (currentDay != tDy) {
String fileName;
String m = String(tMth);
String d = String(tDy);
if (tMth < 10) {
String mm = m;
m = "0" + mm;
}
if (tDy < 10) {
String dd = d;
d = "0" + dd;
}
fileName = m + "-" + d + ".txt";
//String fileName = "01-01.txt";
Serial.println("opening: " + fileName);
File f = SD.open(fileName);
delay(3000);
if (!f) {
Serial.println("open failed");
} else {
while (f.available()) {
String line = f.readStringUntil('\n');
Serial.println(line);
bYearStr = line.substring(0, 4);
bName = line.substring(4);
delay(1000);
currentDay = tDy;
break;
}
f.close();
}
Serial.print("bYearStr: ");
Serial.println(bYearStr);
Serial.print("bName: ");
Serial.println(bName);
int bYear = bYearStr.toInt();
age = tYr - bYear;
Serial.print("Age: ");
Serial.println(age);
} else { // Start current day equal
display.setTextSize(1);
display.setCursor(38, 10);
display.print("Birthday!");
display.println();
display.setTextSize(1);
display.setCursor(0, 30);
display.print("Name: ");
display.print(bName);
display.setCursor(0, 40);
display.print("Age: ");
display.print(age );
display.println();
led = random(3, 6);
dly = random(80, 250);
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(dly); // wait
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
} // End current day
display.display();
} // End void Loop
@E_Ja, 👍1
Обсуждение1 ответ
Вполне вероятно, что у вас нет памяти. Чтобы узнать, действительно нужно следить за указателем стека и структурой кучи. В вашем коде происходит достаточно, чтобы я не мог разумно настроить идентичный сценарий, чтобы доказать это.
Библиотека отображения
Наибольшее использование SRAM в вашем проекте происходит из библиотеки Adafruit SSD1306. Он выделяет буфер для всего кадра, который составляет 128 * 64 == 8192 бит, или 1024 байта, половину SRAM вашего Nano. Он будет использовать больше, чем для других данных, хотя, вероятно, не намного больше в процентах.
Вы можете смягчить это использование с помощью библиотеки, такой как торговля некоторым использованием SRAM для дополнительного времени выполнения или более низких частот кадров. Один из них-библиотекаU8g2 . Вы можете прочитать о том, как он может использовать половину буфера в некоторой документации к более старой версии библиотеки; концепция переносится в более новую библиотеку.
Библиотека SD
Следующее по величине использование-секторный буфер библиотек SD-карт, который составляет 512 байт.
Таким образом, между двумя из них идет 3/4 вашего SRAM только в двух буферах, не думая ни о чем другом.
Вы можете избежать этого буфера, используя другую библиотеку. Вы можете попробовать PETITS. Библиотека, которую он обертывает, говорит, что он использует что-то вроде 44 байт плюс любой стек вызовов.
Строковые литералы
У вас есть по крайней мере 206 байт строковых литералов только в вашем скетче, предполагая, что компилятор не может определить, что ни один из них на самом деле не используется. В библиотеках могут быть и другие, хотя, вероятно, их немного. Итак, теперь у нас осталось по крайней мере 306 байт или меньше SRAM.
Размер | Строка |
---|---|
35 | "RTC потерял мощность, давайте установим время!" |
28 | "Карта провалилась, или нет" |
24 | "Инициализация SD-карты..." |
18 | "Не смог найти RTC" |
18 | - карточка инициализирована. |
12 | "открыть не удалось" |
11 | "bYearStr: " |
10 | "открытие: " |
10 | -День рождения! |
10 | "01-01.txt" |
8 | "bName: " |
7 | "Имя: " |
6 | "Возраст: " |
5 | ".txt" |
2 | "-" |
2 | "0" |
206 | Итого |
"0"
считается только один раз в приведенном выше, хотя у вас их два. Забавный факт: языки C и C++ позволяют идентичным строковым литералам размещаться или перекрываться. Я не знаю , будет ли avr-gcc объединять "Name:"
в "bName:"
, так как у вас есть большие проблемы, о которых нужно беспокоиться, я просто делаю вид, что это не будет, не глядя.
Смягчение может включать использование макроса F()
для перемещения некоторых из этих строковых литералов в PROGMEM (flash). Вы не можете сделать это с каждым строковым литералом, но те, где вы включаете их в строковый(F("мы пытаемся избежать их, верно?"))
конструктор или в последовательный (или другие экземпляры потока/печати) s .print(F("Hello World"));
Кишки макроса F()
- это еще один так называемый PSTR()
, который выполняет фактическую работу по помещению строкового литерала в PROGMEM. F()
помечает полученный указатель фиктивным типом, который используется при перегрузке функций в библиотеках Arduino. Единственная причина, по которой я упоминаю об этом, заключается в том, что ниже вы увидите, как я использую PSTR
для того же самого, только с функцией, которая принадлежит avr-libc, а не Arduino.
Примечание.при инициализации строкового
объекта из обычного строкового литерала стоимость SRAM оплачивается дважды за время существования строкового
объекта. Одна копия находится в SRAM для литерала, используемого для вызова строкового конструктора, другая копия находится в динамической памяти, выделенной строковому объекту.
Класс String
Это было указано в комментариях; и это правда, что лучше избегать их. Хотя, я думаю, на самом деле не самая большая проблема в вашем случае.
Я вижу, что вы избежали ненужного добавления их вместе для последовательного вывода и вместо этого используете отдельные вызовы .print ()
, так что вы впереди игры.
Для вашего имени файла можно использовать локальный массив символов фиксированного размера
и snprintf_P ()
, чтобы собрать ваше имя файла, что-то вроде:
char filename[sizeof "MM-DD.txt"];
snprintf_P(
filename, sizeof filename,
PSTR("%02u-%02u.txt"),
tMth, tDy
);
или если вы хотите избежать втягивания зверя printf
в свой скетч, предполагая, что он уже не там, вы можете сделать что-то вроде:
const char filename[] = {
'0' + tMth / 10,
'0' + tMth % 10,
'-',
'0' + tDy / 10,
'0' + tDy % 10,
'.',
't',
'x',
't',
'\0'
};
Последовательный
Какой Serial
? Да, я имею в виду, что вы мало что можете с этим поделать, но я поднимаю этот вопрос, потому что последняя консервативная цифра выше была 306 байт, и мы вообще не пытались объяснить, что строка делает в куче. Ну, последовательный порт занимает 157 байт в соответствии с простым тестом здесь, что имеет смысл, потому что на UNO он имеет два кольцевых буфера для передачи и приема по 64 байта каждый, полдюжины указателей для настройки аппаратных регистров для использования, а также головные и хвостовые индексы для этих буферов. Есть также еще 12 байт, которые используются для vtable HardwareSerial.
Так что это сводит вас к консервативно 137 байтам SRAM для всего остального.
Все Остальное
Здесь многое не учтено. Ваши глобалы, глобалы библиотек, другие vtables, строковые данные в куче и стеке. Это только те, что приходят на ум. Я имею в виду только переменные счетчика millis()
/micros()
и fract занимают 9 байт. Если вы действительно хотите углубиться в детали здесь, помимо просмотра указателя стека и структуры кучи, команда avr-nm
, выполняемая в файле .elf вашего скетча с его-D
,- S
и --size-sort
, полезна для определения использования глобальных переменных и тому подобных вещей.
На самом деле вы *могли бы* изменить основные файлы (или, возможно, определение платы), чтобы заставить HardwareSerial использовать меньшие буферы. Так что это не полностью выходит из-под вашего контроля. Не уверен, стоит ли его редактировать; он немного ниже в списке., @timemage
- Какие контакты можно использовать для выбора микросхемы (CS, CC) на Arduino Nano Every?
- Понимание того, почему следует избегать «String» и альтернативных решений
- Arduino: как записать значение переменной в текстовый файл и изменить его на SD-карте?
- `.readFileCounts()` не работает в мини-модуле DFPlayer
- Аппаратный SPI Arduino NANO не работает
- Преобразование строки шестнадцатеричных значений Arduino с разделителями-запятыми в массив байтов
- Чтение и запись в EEPROM
- Попытка прочитать случайную строку с SD-карты
Нет больше последовательного вывода после 2 первых символов строки "2020" ? Очень странно. Вы можете упростить свой скетч только в тестовой среде, без всего, что связано с отображением, в конечном итоге даже без RTC., @DataFiddler
Не используйте класс String. Замените его массивами символов. Вероятно, у вас заканчивается объем оперативной памяти., @SBF
Спасибо. @DataFiddler Я сделал это, и это работает с серийной печатью. Скетч также работает, когда я не использую SD-карту, но помещаю даты рождения в меньший тестовый массив (3 имени). Я сталкиваюсь с проблемами с большими массивами (13 имен). Я разобрал его на части и строил заново. Для информации, я вообще не получаю дисплей, похоже, что скрипт останавливается на выходе., @E_Ja
Спасибо, @SBF, я боялся, что именно это и происходит., @E_Ja