Создаю MP3-плеер

Дмиа

✩✩✩✩✩✩✩
11 Янв 2025
2
0
Оформи код соответствующим тэгом, см. Правила
Всем доброго дня! Решил собрать MP-3 плеер на базе arduino nano.
Ответ может быть: используй другие компоненты. Я бы очень хотел выполнить работу с этими компонентами, так как вижу не просто конечное изделие, а получение опыта в работе с дисплеем, слотом, разъемом 3.5 и самой ардуинкой.
Благодарю за помощь и ответы!

Компоненты:

-Arduino nano;
-Oled-дислей с контроллером SSD1306_128x64;
-слот для microSD- карты;
- разъем для minijack 3.5

Проблема:

ошибка компиляции:
Скетч использует 17270 байт (56%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 2338 байт (114%) динамической памяти, оставляя -290 байт для локальных переменных. Максимум: 2048 байт.
Недостаточно памяти; прочитайте https://support.arduino.cc/hc/en-us/articles/360013825179
раздел данных превышает доступное пространство на плате

Compilation error: раздел данных превышает доступное пространство на плате

Сейчас не хватает памяти, а мне необходимо еще вложенное меню в настройки сделать.

Вопрос:

Что в коде "кушает" память?
можно ли отправлять номера пинов кнопок в PROGMEM -память? Если да, то необходимо ли потомкаждый раз использовать функцию чтения изPROGMEM- памяти?
можно ли оптимизировать этот код?

КОД:

#include <GyverOLED.h>
#include <GyverOLEDMenu.h>
#include "GyverButton.h"
#include <SD.h>
#include "SPI.h"
#define btn_choice_delet 2 // выбор; зажать - удалить;
#define btn_record_stop 3 // начать запись; завершение запись – зажать
#define btn_up_back 4 // вверх; зажать вверх - назад
#define btn_down 5 // вниз.
GyverOLED<SSD1306_128x64, OLED_BUFFER> oled;
OledMenu<3, GyverOLED<SSD1306_128x64>> menu(&oled);
GButton buttonChoice(btn_choice_delet);
GButton buttonRecordStop(btn_record_stop);
GButton buttonUpBack(btn_up_back);
GButton buttonDown(btn_down);
const char menuItem1[] PROGMEM = "<- ВЫХОД";
const char menuItem2[] PROGMEM = "Воспроизведение";
const char menuItem3[] PROGMEM = "Настройки";
void setup() {
oled.init();
Wire.setClock(400000L);
oled.clear();
oled.update();
pinMode(btn_choice_delet, INPUT_PULLUP);
pinMode(btn_record_stop, INPUT_PULLUP);
pinMode(btn_up_back, INPUT_PULLUP);
pinMode(btn_down, INPUT_PULLUP);
menu.onChange(onItemChange, true);
menu.addItem(menuItem1);
menu.addItem(menuItem2);
menu.addItem(menuItem3);

menu.showMenu(true);
buttonChoice.setDebounce(20); // Установка задержки для устранения дребезга
buttonRecordStop.setDebounce(20);
buttonUpBack.setDebounce(20);
buttonDown.setDebounce(20);
Serial.begin(9600);
}
void onItemChange(const int index, const void* val, const byte valType) { //Это определение функции onItemChange, которая будет вызываться каждый раз,
// когда пользователь выбирает другой элемент в меню.Функция принимает три параметра:const int index: индекс элемента меню, который был выбран пользователем.
// const void* val: указатель на значение, связанное с элементом меню (может быть использован для передачи дополнительных данных).
// const byte valType: тип значения, переданного в обработчик. В данном случае используется для определения, является ли изменение
// действием (например, выбор элемента).
if (valType == VAL_ACTION) { //Этот условный оператор проверяет, является ли тип значения valType равным VAL_ACTION.
// VAL_ACTION — это, вероятно, константа, определенная в библиотеке, которая указывает, что событие связано с действием (например, выбор элемента меню).
if (index == 0) { //Если тип значения является действием, то проверяется, является ли индекс выбранного элемента равным 0.
// Индекс 0 соответствует первому элементу меню, который в вашем случае — это "<- ВЫХОД".
menu.showMenu(false); //Если индекс равен 0, вызывается метод showMenu(false), который, вероятно, закрывает текущее меню или скрывает его.
// Параметр false может указывать, что меню должно быть скрыто, а не показано.
}
}
}
void loop() {
Btn_H ();
}
void Btn_H () {
buttonChoice.tick();
buttonRecordStop.tick();
buttonUpBack.tick();
buttonDown.tick();
// Обработка кнопок для перемещения по меню
if (buttonUpBack.isPress()) { // Если нажата кнопка "вверх"
menu.selectPrev(); // Используем selectPrev() для перемещения вверх
}
if (buttonDown.isPress()) { // Если нажата кнопка "вниз"
menu.selectNext(); // Используем selectNext() для перемещения вниз
}
// Проверка нажатия кнопки выбора
if (buttonChoice.isPress()) {
// Здесь можно добавить код для обработки выбора
Serial.println("Кнопка выбора нажата");
}
// Обновляем меню
// menu.tick(); // Используем tick() для обновления состояния меню
delay(50); // Небольшая задержка для устранения дребезга
}
 

Вложения

  • 50.1 KB Просмотры: 5

Bruzzer

★★★✩✩✩✩
23 Май 2020
508
149
получение опыта в работе с дисплеем, слотом, разъемом 3.5 и самой ардуинкой.
Делайте отдельные скетчи и смотрите, сколько памяти используется
  • Работа с дисплеем
  • Работа с SD картой
  • Работа с еще чем то.
Дисплей и SD требуют довольно много памяти.
Простой способ взять другой МК.
Менее простой - использовать библиотеки с меньшими требованиями к памяти. Если дисплей в текстовом режиме, то есть библиотеки использующие меньший буфер например https://github.com/olikraus/u8g2/wiki вариант U8x8. Есть и другие работающие с меньшим буфером, Гайвер тоже заявляет о такой возможности своей библиотеки. Но в этом надо разбираться, искать и смотреть примеры, проверять работоспособность на своем экране и т.д.

П.С. Не понятно, как вы планируете проигрывать MP3 на NANO. Возможно только WAV, и потребуется еще память под буфер.
И в целом, если это реализуемо, то в Инете должны быть готовые проекты.
 
  • Красота! +2
Реакции: Дмиа

viktor1703

★★★✩✩✩✩
9 Дек 2021
647
153
C++:
#include <GyverOLED.h>
#include <GyverOLEDMenu.h>
#include "GyverButton.h"
#include <SD.h>
#include "SPI.h"
#define btn_choice_delet 2 // выбор; зажать - удалить;
#define btn_record_stop 3 // начать запись; завершение запись – зажать
#define btn_up_back 4 // вверх; зажать вверх - назад
#define btn_down 5 // вниз.
GyverOLED<SSD1306_128x64, OLED_BUFFER> oled;
OledMenu<3, GyverOLED<SSD1306_128x64>> menu(&oled);
GButton buttonChoice(btn_choice_delet);
GButton buttonRecordStop(btn_record_stop);
GButton buttonUpBack(btn_up_back);
GButton buttonDown(btn_down);
const char menuItem1[] PROGMEM = "<- ВЫХОД";
const char menuItem2[] PROGMEM = "Воспроизведение";
const char menuItem3[] PROGMEM = "Настройки";
void setup() {
oled.init();
Wire.setClock(400000L);
oled.clear();
oled.update();
pinMode(btn_choice_delet, INPUT_PULLUP);
pinMode(btn_record_stop, INPUT_PULLUP);
pinMode(btn_up_back, INPUT_PULLUP);
pinMode(btn_down, INPUT_PULLUP);
menu.onChange(onItemChange, true);
menu.addItem(menuItem1);
menu.addItem(menuItem2);
menu.addItem(menuItem3);

menu.showMenu(true);
buttonChoice.setDebounce(20); // Установка задержки для устранения дребезга
buttonRecordStop.setDebounce(20);
buttonUpBack.setDebounce(20);
buttonDown.setDebounce(20);
Serial.begin(9600);
}
void onItemChange(const int index, const void* val, const byte valType) { //Это определение функции onItemChange, которая будет вызываться каждый раз,
// когда пользователь выбирает другой элемент в меню.Функция принимает три параметра:const int index: индекс элемента меню, который был выбран пользователем.
// const void* val: указатель на значение, связанное с элементом меню (может быть использован для передачи дополнительных данных).
// const byte valType: тип значения, переданного в обработчик. В данном случае используется для определения, является ли изменение
// действием (например, выбор элемента).
if (valType == VAL_ACTION) { //Этот условный оператор проверяет, является ли тип значения valType равным VAL_ACTION.
// VAL_ACTION — это, вероятно, константа, определенная в библиотеке, которая указывает, что событие связано с действием (например, выбор элемента меню).
if (index == 0) { //Если тип значения является действием, то проверяется, является ли индекс выбранного элемента равным 0.
// Индекс 0 соответствует первому элементу меню, который в вашем случае — это "<- ВЫХОД".
menu.showMenu(false); //Если индекс равен 0, вызывается метод showMenu(false), который, вероятно, закрывает текущее меню или скрывает его.
// Параметр false может указывать, что меню должно быть скрыто, а не показано.
}
}
}
void loop() {
Btn_H ();
}
void Btn_H () {
buttonChoice.tick();
buttonRecordStop.tick();
buttonUpBack.tick();
buttonDown.tick();
// Обработка кнопок для перемещения по меню
if (buttonUpBack.isPress()) { // Если нажата кнопка "вверх"
menu.selectPrev(); // Используем selectPrev() для перемещения вверх
}
if (buttonDown.isPress()) { // Если нажата кнопка "вниз"
menu.selectNext(); // Используем selectNext() для перемещения вниз
}
// Проверка нажатия кнопки выбора
if (buttonChoice.isPress()) {
// Здесь можно добавить код для обработки выбора
Serial.println("Кнопка выбора нажата");
}
// Обновляем меню
// menu.tick(); // Используем tick() для обновления состояния меню
delay(50); // Небольшая задержка для устранения дребезга
}
Так получше. Но, разве это весь код? Если, да, то бросьте это дело, и берите другой контроллер, с большей памятью. Если у вас в приведенном коде не хватает памяти для опроса кнопок и отображения меню, то куда вы собираетесь читать данные из SD, обрабатывать и еще выдавать в ШИМ?
C++:
delay(50); // Небольшая задержка для устранения дребезга
Это как будет музыка играть, если вы предлагаете ей затыкаться на 50 мс?
 
  • Красота! +2
Реакции: Дмиа

Zuker

★✩✩✩✩✩✩
10 Янв 2024
66
14
У вас как минимум дисплейчик жрет много памяти работая с буфером (1кб). Попробуйте без него, замените:
C++:
GyverOLED<SSD1306_128x64, OLED_BUFFER> oled;
на
C++:
GyverOLED<SSD1306_128x64, OLED_NO_BUFFER> oled;
И перепроверьте всю свою рисовку. Возможны нюансы. Но все можно обыграть.
 
  • Красота! +2
Реакции: Дмиа

Дмиа

✩✩✩✩✩✩✩
11 Янв 2025
2
0
@viktor1703,
Благодарю за ответ, к сожалению, также требует освобождения памяти!!!!!!!!

Это не весь код,тут только начало, далее буду развивать подключение сд-слота, джек3.5.

"Если у вас в приведенном коде не хватает памяти для опроса кнопок и отображения меню, то куда вы собираетесь читать данные из SD, обрабатывать и еще выдавать в ШИМ? "

Соглашусь с Вами, но динамической памяти не хватает,ее там 2 килобайта, а PROGMEM 32, очень весомо получается, я бы хотел разобраться всё-таки в особенностях памяти, чего никак не могу понять,хоть и обучали Гайвера читал, смотрел.

@Zuker,
Спасибо, немного памяти освободилось:)))

Скетч использует 17714 байт (57%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 1314 байт (64%) динамической памяти, оставляя 734 байт для локальных переменных. Максимум: 2048 байт.

@Bruzzer,
Блин, точняк! Благодарю!
 

viktor1703

★★★✩✩✩✩
9 Дек 2021
647
153
Если использовать латиницу, то можно ещё несколько байт освободить