Считывание с карты micro sd

Шаромонстр

✩✩✩✩✩✩✩
15 Фев 2023
12
0
Всем привет! Я в ардуино совсем новичок, помогите, пожалуйста, разобраться.
Есть ардуино нано и скетч, который выводит картинки из текстового файла на светодиодную ленту. Но загрузить в ардуино можно не больше трёх картинок. Иначе скетч получается слишком большой и не загружается на ардуино.
У меня есть модуль micro sd и карта. Можно ли сделать, чтобы ардуино просто брал файл из карты и включал его данные в программу?
 

Шаромонстр

✩✩✩✩✩✩✩
15 Фев 2023
12
0
@Wan-Derer, есть сторонний файл, в котором содержатся параметры изображений и к которому скетч обращается во время работы. Проблем не возникает, если в файле 2-3 изображения и этот файл в той же папке, что и скетч.
Нужно, чтобы в файле было много изображений. Но тогда его объём очень большой. И необходимо, чтобы скетч во время работы открывал файл с карты sd и обращался к этим параметрам. И все эти изображения потом выводились на светодиодную ленту.
проблема в том, что просто открыть файл недостаточно (что и логично). А как сделать, чтобы скетч обращался к этому файлу на карте я не знаю((
 

PiratFox

★★★★★✩✩
13 Фев 2020
1,703
474
есть сторонний файл, в котором содержатся параметры изображений и к которому скетч обращается во время работы.
Вот тут непонятно, как этот файл попал на SD карту. Вы его туда записали кардридером с компьютера средствами ОС, или каким-то образом пытаетесь это сделать ардуиной?
 
Изменено:

bort707

★★★★★★✩
21 Сен 2020
3,094
916
Давайте я вам ТС переведу, как я его понял.

есть сторонний файл, в котором содержатся параметры изображений и к которому скетч обращается во время работы.
я думаю это файл .h, который лежит рядом со скетчем в директории и в котором записаны массивы картинок. Чтоб его "прочитать", его достаточно подключить к скетчу.

А потом он пытается точно такой же файл положить на СД -карту... Но у него, естесственно, ничего не выходит:
проблема в том, что просто открыть файл недостаточно (что и логично)
 

bort707

★★★★★★✩
21 Сен 2020
3,094
916
В первую очередь - формат файлов для подключения к коду в виде хидера (.h файл) и для хранения данных на СД - разный. Можно, конечно, пытаться и с карты читать хидер файл - но это примерно тройная работа по сравнению с обычными данными.

Во вторых - файл мало открыть, из него надо читать данные и сохранять прочитанное в массив в памяти. Как это делать - есть в примерах к библиотеке СД карты.

В третьих, что за ардуина у вас? Если Уно или Нано, то можете сразу бросить эту затею. У них очень мало памяти, а нужен и буфер СД карточки, и массив для вывода на ленту... а значит вы даже одну картинку из тех что были в отдельном файле - за раз с карты не прочитаете. Если только читать совсем маленькими порциями и сразу посылать на ленту - но это может и не работать, смотря какие картинки.
 

Шаромонстр

✩✩✩✩✩✩✩
15 Фев 2023
12
0
вот сам рабочий скетч (не мой естественно) и файл, который хотел бы вставить в скетч.
Скетч получается слишком большой. Я не знаю, как с помощью sd карты и модуля сделать так, чтобы все работало.
Ардуино Нано

C++:
#include <Arduino.h>
//#include <Adafruit_DotStar.h>
#include <avr/power.h>
#include <avr/sleep.h>
#include <SPI.h> // Enable this line on Pro Trinket


//#include "FastLED.h"
#include "Adafruit_NeoPixel.h"


#ifdef __AVR_ATtiny85__
typedef uint8_t  line_t; // Max 255 lines/image on Trinket
#else
typedef uint16_t line_t; // Bigger images OK on other boards
#endif

// CONFIGURABLE STUFF ------------------------------------------------------

#include "026.h"


#define DATA_PIN 4 // digital pin of your arduino
#define NUM_LEDS 36 // number of led present in your strip
#define LED_BR 150



// Select from multiple images using tactile button (#1489) between pin and
// ground.  Requires suitably-built graphics.h file w/more than one image.
#define SELECT_PIN 3

// Optional feature -- not enabled here, no space -- a vibration switch
// (aligned perpendicular to leash) is used as a poor man's accelerometer.
// Poi then lights only when moving, saving some power.  The 'fast'
// vibration switch is VERY sensitive and will trigger at the slightest
// bump, while the 'medium' switch requires a certain spin rate which may
// not trigger if you're doing mellow spins.  Neither is perfect.  To leave
// that out and simply have the poi run always-on, comment out this line:
//#define MOTION_PIN 2

// Another optional feature not enable due to physical size -- powering down
// DotStars when idle conserves more battery.  Use a PNP transistor (e.g.
// 2N2907) (w/220 Ohm resistor to base) as a 'high side' switch to DotStar
// +V.  DON'T do this NPN/low-side, may damage strip.  MOTION_PIN must also
// be defined to use this (pointless without).
//#define POWER_PIN 4

#define SLEEP_TIME 5000   // Not-spinning time before sleep, in milliseconds

// Empty and full thresholds (millivolts) used for battery level display:
#define BATT_MIN_MV 3350  // Some headroom over battery cutoff near 2.9V
#define BATT_MAX_MV 4000  // And little below fresh-charged battery near 4.1V
// These figures are based on LiPoly cell and will need to be tweaked for
// 3X NiMH or alkaline batteries!



boolean autoCycle = true; // Set to true to cycle images by default
#define CYCLE_TIME 10     // Time, in seconds, between auto-cycle images

// -------------------------------------------------------------------------

//Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);



#if defined(DATA_PIN)                

//#if defined(LED_DATA_PIN) && defined(LED_CLOCK_PIN)
// Older DotStar LEDs use GBR order.  If colors are wrong, edit here.

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);
//Adafruit_DotStar strip = Adafruit_DotStar(NUM_LEDS, LED_DATA_PIN, LED_CLOCK_PIN, DOTSTAR_BRG);


#else
///Adafruit_DotStar strip = Adafruit_DotStar(NUM_LEDS, DOTSTAR_BGR);
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, NEO_GRB + NEO_KHZ800);


#endif

void     imageInit(void);
uint16_t readVoltage(void);
#ifdef MOTION_PIN
void     sleep(void);
#endif

void setup() {

  strip.setBrightness(LED_BR);
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L)
  clock_prescale_set(clock_div_1);   // Enable 16 MHz on Trinket
#endif

#ifdef POWER_PIN
  pinMode(POWER_PIN, OUTPUT);
  digitalWrite(POWER_PIN, LOW); // Power-on LED strip
#endif
  strip.begin();                // Allocate DotStar buffer, init SPI                    
  strip.clear();                // Make sure strip is clear
  strip.show();                 // before measuring battery

  // Display battery level bargraph on startup.  It's just a vague estimate
  // based on cell voltage (drops with discharge) but doesn't handle curve.
  uint16_t mV  = readVoltage();
  uint8_t  lvl = (mV >= BATT_MAX_MV) ? NUM_LEDS : // Full (or nearly)
                 (mV <= BATT_MIN_MV) ?        1 : // Drained
                 1 + ((mV - BATT_MIN_MV) * NUM_LEDS + (NUM_LEDS / 2)) /
                 (BATT_MAX_MV - BATT_MIN_MV + 1); // # LEDs lit (1-NUM_LEDS)
  for(uint8_t i=0; i<lvl; i++) {                  // Each LED to batt level...
    uint8_t g = (i * 5 + 2) / NUM_LEDS;           // Red to green
    strip.setPixelColor(i, 4-g, g, 0);                                      
    strip.show();                                 // Animate a bit
    delay(250 / NUM_LEDS);
  }
  delay(200);                                    // Hold last state a moment
  strip.clear();                                  // Then clear strip
  strip.show();

  imageInit(); // Initialize pointers for default image

#ifdef SELECT_PIN
  pinMode(SELECT_PIN, INPUT_PULLUP);
#endif
#ifdef MOTION_PIN
  pinMode(MOTION_PIN, INPUT_PULLUP);
  sleep();     // Sleep until motion detected
#endif
}

// GLOBAL STATE STUFF ------------------------------------------------------

uint32_t lastImageTime = 0L; // Time of last image change
#ifdef MOTION_PIN
uint32_t prev          = 0L; // Used for sleep timing
#endif
uint8_t  imageNumber   = 0,  // Current image being displayed
         imageType,          // Image type: PALETTE[1,4,8] or TRUECOLOR
        *imagePalette,       // -> palette data in PROGMEM
        *imagePixels,        // -> pixel data in PROGMEM
         palette[16][3];     // RAM-based color table for 1- or 4-bit images
line_t   imageLines,         // Number of lines in active image
         imageLine;          // Current line number in image
#ifdef SELECT_PIN
uint8_t  debounce      = 0;  // Debounce counter for image select pin
#endif

void imageInit() { // Initialize global image state for current imageNumber
  imageType    = pgm_read_byte(&images[imageNumber].type);
#ifdef __AVR_ATtiny85__
  imageLines   = pgm_read_byte(&images[imageNumber].lines);
#else
  imageLines   = pgm_read_word(&images[imageNumber].lines);
#endif
  imageLine    = 0;
  imagePalette = (uint8_t *)pgm_read_word(&images[imageNumber].palette);
  imagePixels  = (uint8_t *)pgm_read_word(&images[imageNumber].pixels);
  // 1- and 4-bit images have their color palette loaded into RAM both for
  // faster access and to allow dynamic color changing.  Not done w/8-bit
  // because that would require inordinate RAM (328P could handle it, but
  // I'd rather keep the RAM free for other features in the future).
  if(imageType == PALETTE1)      memcpy_P(palette, imagePalette,  2 * 3);
  else if(imageType == PALETTE4) memcpy_P(palette, imagePalette, 16 * 3);
  lastImageTime = millis(); // Save time of image init for next auto-cycle
}

void nextImage(void) {
  if(++imageNumber >= NUM_IMAGES) imageNumber = 0;
  imageInit();
}

// MAIN LOOP ---------------------------------------------------------------

void loop() {
  uint32_t t = millis();               // Current time, milliseconds
#ifdef MOTION_PIN
  // Tried to do this with watchdog timer but encountered gas pains, so...
  if(!digitalRead(MOTION_PIN)) {       // Vibration switch pulled down?
    prev = t;                          // Yes, reset timer
  } else if((t - prev) > SLEEP_TIME) { // No, SLEEP_TIME elapsed w/no switch?
    sleep();                           // Power down
    prev = t;                          // Reset timer on wake
  }
#endif

  if(autoCycle) {
    if((t - lastImageTime) >= (CYCLE_TIME * 1000L)) nextImage();
    // CPU clocks vary slightly; multiple poi won't stay in perfect sync.
    // Keep this in mind when using auto-cycle mode, you may want to cull
    // the image selection to avoid unintentional regrettable combinations.
  }
#ifdef SELECT_PIN
  if(digitalRead(SELECT_PIN)) {        // Image select?
    debounce = 0;                      // Not pressed -- reset counter
  } else {                             // Pressed...
    if(++debounce >= 25) {             // Debounce input
      nextImage();                     // Switch to next image
      while(!digitalRead(SELECT_PIN)); // Wait for release
      // If held 1+ sec, toggle auto-cycle mode on/off
      if((millis() - t) >= 1000L) autoCycle = !autoCycle;
      debounce = 0;
    }
  }
#endif

  // Transfer one scanline from pixel data to LED strip:

  // If you're really pressed for graphics space and need just a few extra
  // scanlines, and know for a fact you won't be using certain image modes,
  // you can comment out the corresponding blocks below.  e.g. PALETTE8 and
  // TRUECOLOR are somewhat impractical on Trinket, and commenting them out
  // can free up nearly 200 bytes of extra image storage.

  switch(imageType) {

    case PALETTE1: { // 1-bit (2 color) palette-based image
      uint8_t  pixelNum = 0, byteNum, bitNum, pixels, idx,
              *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS / 8];
      for(byteNum = NUM_LEDS/8; byteNum--; ) { // Always padded to next byte
        pixels = pgm_read_byte(ptr++);  // 8 pixels of data (pixel 0 = LSB)
        for(bitNum = 8; bitNum--; pixels >>= 1) {
          idx = pixels & 1; // Color table index for pixel (0 or 1)
          strip.setPixelColor(pixelNum++,                                              /////////////////////////////
            palette[idx][0], palette[idx][1], palette[idx][2]);
        }
      }
      break;
    }

    case PALETTE4: { // 4-bit (16 color) palette-based image
      uint8_t  pixelNum, p1, p2,
              *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS / 2];
      for(pixelNum = 0; pixelNum < NUM_LEDS; ) {
        p2  = pgm_read_byte(ptr++); // Data for two pixels...
        p1  = p2 >> 4;              // Shift down 4 bits for first pixel
        p2 &= 0x0F;                 // Mask out low 4 bits for second pixel
        strip.setPixelColor(pixelNum++,                                    ////////////////////////
          palette[p1][0], palette[p1][1], palette[p1][2]);
        strip.setPixelColor(pixelNum++,                                    /////////////////////
          palette[p2][0], palette[p2][1], palette[p2][2]);
      }
      break;
    }

    case PALETTE8: { // 8-bit (256 color) PROGMEM-palette-based image
      uint16_t  o;
      uint8_t   pixelNum,
               *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS];
      for(pixelNum = 0; pixelNum < NUM_LEDS; pixelNum++) {
        o = pgm_read_byte(ptr++) * 3; // Offset into imagePalette
        strip.setPixelColor(pixelNum,                                  /////////////////////
          pgm_read_byte(&imagePalette[o]),
          pgm_read_byte(&imagePalette[o + 1]),
          pgm_read_byte(&imagePalette[o + 2]));
      }
      break;
    }

    case TRUECOLOR: { // 24-bit ('truecolor') image (no palette)
      uint8_t  pixelNum, r, g, b,
              *ptr = (uint8_t *)&imagePixels[imageLine * NUM_LEDS * 3];
      for(pixelNum = 0; pixelNum < NUM_LEDS; pixelNum++) {
        r = pgm_read_byte(ptr++);
        g = pgm_read_byte(ptr++);
        b = pgm_read_byte(ptr++);
        strip.setPixelColor(pixelNum, r, g, b);                              
      }
      break;
    }
  }



  strip.show(); // Refresh LEDs
#if !defined(DATA_PIN) //&& !defined(LED_CLOCK_PIN)
  delayMicroseconds(900);  // Because hardware SPI is ludicrously fast
#endif
  if(++imageLine >= imageLines) imageLine = 0; // Next scanline, wrap around
}

// POWER-SAVING STUFF -- Relentlessly non-portable -------------------------

#ifdef MOTION_PIN
void sleep() {

  // Turn off LEDs...
  strip.clear();                 // Issue '0' data
  strip.show();
#ifdef POWER_PIN                                                              
  digitalWrite(POWER_PIN, HIGH); // Cut power
#if !defined(DATA_PIN) //&& !defined(LED_CLOCK_PIN)
#ifdef __AVR_ATtiny85__
  pinMode(1, INPUT);             // Set SPI data & clock to inputs else
  pinMode(2, INPUT);             // DotStars power parasitically, jerks.
#else
  pinMode(11, INPUT);
  pinMode(13, INPUT);
#endif // ATtiny
#endif // Data/clock/pins
#endif // POWER_PIN

  power_all_disable(); // Peripherals ALL OFF, best sleep-state battery use

  // Enable pin-change interrupt on motion pin
#ifdef __AVR_ATtiny85__
  PCMSK = _BV(MOTION_PIN);  // Pin mask
  GIMSK = _BV(PCIE);        // Interrupt enable
#else
  volatile uint8_t *p = portInputRegister(digitalPinToPort(MOTION_PIN));
  if(p == &PIND) {          // Pins 0-7 = PCINT16-23
    PCMSK2 = _BV(MOTION_PIN);
    PCICR  = _BV(PCIE2);
  } else if(p == &PINB) {   // Pins 8-13 = PCINT0-5
    PCMSK0 = _BV(MOTION_PIN- 8);
    PCICR  = _BV(PCIE0);
  } else if(p == &PINC) {   // Pins 14-20 = PCINT8-14
    PCMSK1 = _BV(MOTION_PIN-14);
    PCICR  = _BV(PCIE1);
  }
#endif

  // If select pin is enabled, that wakes too!
#ifdef SELECT_PIN
  debounce = 0;
#ifdef __AVR_ATtiny85__
  PCMSK |= _BV(SELECT_PIN); // Add'l pin mask
#else
  volatile uint8_t *p = portInputRegister(digitalPinToPort(SELECT_PIN));
  if(p == &PIND) {        // Pins 0-7 = PCINT16-23
    PCMSK2 = _BV(SELECT_PIN);
    PCICR  = _BV(PCIE2);
  } else if(p == &PINB) { // Pins 8-13 = PCINT0-5
    PCMSK0 = _BV(SELECT_PIN- 8);
    PCICR  = _BV(PCIE0);
  } else if(p == &PINC) { // Pins 14-20 = PCINT8-14
    PCMSK1 = _BV(SELECT_PIN-14);
    PCICR  = _BV(PCIE1);
  }
#endif // ATtiny
#endif // SELECT_PIN

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Deepest sleep mode
  sleep_enable();
  interrupts();
  sleep_mode();                        // Power down

  // Resumes here on wake

  // Clear pin change settings so interrupt won't fire again
#ifdef __AVR_ATtiny85__
  GIMSK = PCMSK = 0;
#else
  PCICR = PCMSK0 = PCMSK1 = PCMSK2 = 0;
#endif
  power_timer0_enable();        // Used by millis()



#if !defined(DATA_PIN) //&& !defined(LED_CLOCK_PIN)

#ifdef __AVR_ATtiny85__
  pinMode(1, OUTPUT);           // Re-enable SPI pins
  pinMode(2, OUTPUT);
  power_usi_enable();           // Used by DotStar
#else
  pinMode(11, OUTPUT);          // Re-enable SPI pins
  pinMode(13, OUTPUT);
  power_spi_enable();           // Used by DotStar
#endif // ATtiny
#endif // Data/clock pins
#ifdef POWER_PIN
  digitalWrite(POWER_PIN, LOW); // Power-up LEDs
#endif
  prev = millis();              // Save wake time
}

EMPTY_INTERRUPT(PCINT0_vect); // Pin change (does nothing, but required)
#ifndef __AVR_ATtiny85__
ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));
ISR(PCINT2_vect, ISR_ALIASOF(PCINT0_vect));
#endif

#endif // MOTION_PIN

// Battery monitoring idea adapted from JeeLabs article:
// jeelabs.org/2012/05/04/measuring-vcc-via-the-bandgap/
// Code from Adafruit TimeSquare project, added Trinket support.
// In a pinch, the poi code can work on a 3V Trinket, but the battery
// monitor will not work correctly (due to the 3.3V regulator), so
// maybe just comment out any reference to this code in that case.
uint16_t readVoltage() {
  int      i, prev;
  uint8_t  count;
  uint16_t mV;

  // Select AVcc voltage reference + Bandgap (1.8V) input
#ifdef __AVR_ATtiny85__
  ADMUX  = _BV(MUX3) | _BV(MUX2);
#else
  ADMUX  = _BV(REFS0) |
           _BV(MUX3)  | _BV(MUX2) | _BV(MUX1);
#endif
  ADCSRA = _BV(ADEN)  |                          // Enable ADC
           _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 1/128 prescaler (125 KHz)
  // Datasheet notes that the first bandgap reading is usually garbage as
  // voltages are stabilizing.  It practice, it seems to take a bit longer
  // than that.  Tried various delays, but still inconsistent and kludgey.
  // Instead, repeated readings are taken until four concurrent readings
  // stabilize within 10 mV.
  for(prev=9999, count=0; count<4; ) {
    for(ADCSRA |= _BV(ADSC); ADCSRA & _BV(ADSC); ); // Start, await ADC conv.
    i  = ADC;                                       // Result
    mV = i ? (1100L * 1023 / i) : 0;                // Scale to millivolts
    if(abs((int)mV - prev) <= 10) count++;   // +1 stable reading
    else                          count = 0; // too much change, start over
    prev = mV;
  }
  ADCSRA = 0; // ADC off
  return mV;
}
@bort707, nano, а как же тогда делают подобные портативные светящиеся штуки, которые занимают мало места, но в которые можно загружать большое количество картинок?
 

Вложения

  • 343.3 KB Просмотры: 6

bort707

★★★★★★✩
21 Сен 2020
3,094
916
все еще хуже, у вас в файле не только данные, но и код - описание структур.
C++:
typedef struct {
  uint8_t        type;    // PALETTE[1,4,8] or TRUECOLOR
  line_t         lines;   // Length of image (in scanlines
  const uint8_t *palette; // -> PROGMEM color table (NULL if truecolor
  const uint8_t *pixels;  // -> Pixel data in PROGMEM
} image;

const image PROGMEM images[] = {
  { TRUECOLOR,   78, NULL                      , pixels00 },
  { TRUECOLOR,   60, NULL                      , pixels01 },
  { TRUECOLOR,  123, NULL                      , pixels02 },
  { TRUECOLOR,   95, NULL                      , pixels03 },
  { TRUECOLOR,  128, NULL                      , pixels04 },
  { TRUECOLOR,   45, NULL                      , pixels05 }};

#define NUM_IMAGES (sizeof(images) / sizeof(images[0]))
Считать исходный код с карты и потом запустить как программу - в принципе невозможно, С не интерпретируемый язык.
Вам надо сначала разделить данные и структуры, структуры вставить в программу, данные записать на карту в виде бинарного файла.... короче бросьте.
 

bort707

★★★★★★✩
21 Сен 2020
3,094
916
как же тогда делают подобные портативные светящиеся штуки, которые занимают мало места, но в которые можно загружать большое количество картинок?
Как лампа гайвера?
Там обычно стоят контроллеры с большей памятью, например ESP8266, ESp32 или СТМ
 

PiratFox

★★★★★✩✩
13 Фев 2020
1,703
474
@Шаромонстр, если дело обстоит так, как описал @bort707, тогда в целом понятно, где собака порылась. Нужно переписывать процедуры в самом скетче, что сделать без опыта ну ооочень проблематично. Тут я согласен с тов. @bort707,
.... короче бросьте.
 

bort707

★★★★★★✩
21 Сен 2020
3,094
916
@Шаромонстр, красиво.

Откажитесь от нано, возьмите например RP2040 - чуть дороже, а памяти в 150 раз больше. Там вам даже карточка может не понадобится, тут на прямо платеот 4 до 16 Мб флеша распаяно.
RP2040 можно программировать в Ардуино ИДЕ
 
  • Лойс +1
Реакции: Шаромонстр

Шаромонстр

✩✩✩✩✩✩✩
15 Фев 2023
12
0
@bort707, супер! Спасибо большое! Посмотрел на сайте, откуда брал скетч, оказывается там вообще Teensy 3.2. А в магазине радиодеталей решили дать nano😅 а я не разбираюсь, начал всё это изучать только когда стал собирать и даже не думал, что могут быть и другие более ресурсоемкие контроллеры

@bort707, спасибо большое! заказал на 16 mb, думаю, картинок на 500-600 точно хватит😅