text:
Can I record us (microseconds) with AnalogBinLogger example in SdFat's library?
I am using the AnalogBinLogger from the SdFat example library. I  am using it to record 8 analog channels at 5kHz! which is amazing (thank you SdFat).
Is it possible to include a separate column to provide a time-stamp in microseconds for when the samples were recorded? Or will this impair the speed at which I can log data?
Or; how reliable is the sample rate? To what degree of confidence can I assume that the sketch executes (in my case) every 200 microseconds?
I'm performing frequency analysis on the data so knowing when the data is captured or if there is any drift or skew is important to me.
Thank you in advance Arduino Crew!
...appended...
To answer @jsotola:
The sketch I am using (see below) comes with the SdFat library, which is beyond my basic understanding of code. I can write a for loop and an if statement but the stuff that goes on in this sketch is levels above where I am at right now. Trust me, if I could've I would've.
To answer @Gerben:
The 5kHz sampling rate is the default sampling rate that comes with the example sketch. As long as I can be confident that the samples are taken every 200us then I wont need the timestamp but I don't know how confident I can be or how to write a timestamp into the code.
/**
 * This program logs data from the Arduino ADC to a binary file.
 *
 * Samples are logged at regular intervals. Each Sample consists of the ADC
 * values for the analog pins defined in the PIN_LIST array.  The pins numbers
 * may be in any order.
 *
 * Edit the configuration constants below to set the sample pins, sample rate,
 * and other configuration values.
 *
 * If your SD card has a long write latency, it may be necessary to use
 * slower sample rates.  Using a Mega Arduino helps overcome latency
 * problems since 13 512 byte buffers will be used.
 *
 * Each 512 byte data block in the file has a four byte header followed by up
 * to 508 bytes of data. (508 values in 8-bit mode or 254 values in 10-bit mode)
 * Each block contains an integral number of samples with unused space at the
 * end of the block.
 *
 * Data is written to the file using a SD multiple block write command.
 */
#ifdef __AVR__
#include <SPI.h>
#include "SdFat.h"
#include "FreeStack.h"
#include "AnalogBinLogger.h"
//------------------------------------------------------------------------------
// Analog pin number list for a sample.  Pins may be in any order and pin
// numbers may be repeated.
const uint8_t PIN_LIST[] = {0, 1, 2, 3, 4, 5, 6, 7};
//------------------------------------------------------------------------------
// Sample rate in samples per second.
const float SAMPLE_RATE = 5000;  // Must be 0.25 or greater.

// The interval between samples in seconds, SAMPLE_INTERVAL, may be set to a
// constant instead of being calculated from SAMPLE_RATE.  SAMPLE_RATE is not
// used in the code below.  For example, setting SAMPLE_INTERVAL = 2.0e-4
// will result in a 200 microsecond sample interval.
const float SAMPLE_INTERVAL = 1.0/SAMPLE_RATE;

// Setting ROUND_SAMPLE_INTERVAL non-zero will cause the sample interval to
// be rounded to a a multiple of the ADC clock period and will reduce sample
// time jitter.
#define ROUND_SAMPLE_INTERVAL 1
//------------------------------------------------------------------------------
// ADC clock rate.
// The ADC clock rate is normally calculated from the pin count and sample
// interval.  The calculation attempts to use the lowest possible ADC clock
// rate.
//
// You can select an ADC clock rate by defining the symbol ADC_PRESCALER to
// one of these values.  You must choose an appropriate ADC clock rate for
// your sample interval.
// #define ADC_PRESCALER 7 // F_CPU/128 125 kHz on an Uno
// #define ADC_PRESCALER 6 // F_CPU/64  250 kHz on an Uno
// #define ADC_PRESCALER 5 // F_CPU/32  500 kHz on an Uno
// #define ADC_PRESCALER 4 // F_CPU/16 1000 kHz on an Uno
// #define ADC_PRESCALER 3 // F_CPU/8  2000 kHz on an Uno (8-bit mode only)
//------------------------------------------------------------------------------
// Reference voltage.  See the processor data-sheet for reference details.
// uint8_t const ADC_REF = 0; // External Reference AREF pin.
uint8_t const ADC_REF = (1 << REFS0);  // Vcc Reference.
// uint8_t const ADC_REF = (1 << REFS1);  // Internal 1.1 (only 644 1284P Mega)
// uint8_t const ADC_REF = (1 << REFS1) | (1 << REFS0);  // Internal 1.1 or 2.56
//------------------------------------------------------------------------------
// File definitions.
//
// Maximum file size in blocks.
// The program creates a contiguous file with FILE_BLOCK_COUNT 512 byte blocks.
// This file is flash erased using special SD commands.  The file will be
// truncated if logging is stopped early.
const uint32_t FILE_BLOCK_COUNT = 256000;

// log file base name.  Must be six characters or less.
#define FILE_BASE_NAME "analog"

// Set RECORD_EIGHT_BITS non-zero to record only the high 8-bits of the ADC.
#define RECORD_EIGHT_BITS 0
//------------------------------------------------------------------------------
// Pin definitions.
//
// Digital pin to indicate an error, set to -1 if not used.
// The led blinks for fatal errors. The led goes on solid for SD write
// overrun errors and logging continues.
const int8_t ERROR_LED_PIN = 3;

// SD chip select pin.
const uint8_t SD_CS_PIN = SS;
//------------------------------------------------------------------------------
// Buffer definitions.
//
// The logger will use SdFat's buffer plus BUFFER_BLOCK_COUNT additional
// buffers.  QUEUE_DIM must be a power of two larger than
//(BUFFER_BLOCK_COUNT + 1).
//
#if RAMEND < 0X8FF
#error Too little SRAM
//
#elif RAMEND < 0X10FF
// Use total of two 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 1;
// Dimension for queues of 512 byte SD blocks.
const uint8_t QUEUE_DIM = 4;  // Must be a power of two!
//
#elif RAMEND < 0X20FF
// Use total of five 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 4;
// Dimension for queues of 512 byte SD blocks.
const uint8_t QUEUE_DIM = 8;  // Must be a power of two!
//
#elif RAMEND < 0X40FF
// Use total of 13 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 12;
// Dimension for queues of 512 byte SD blocks.
const uint8_t QUEUE_DIM = 16;  // Must be a power of two!
//
#else  // RAMEND
// Use total of 29 512 byte buffers.
const uint8_t BUFFER_BLOCK_COUNT = 28;
// Dimension for queues of 512 byte SD blocks.
const uint8_t QUEUE_DIM = 32;  // Must be a power of two!
#endif  // RAMEND
//==============================================================================
// End of configuration constants.
//==============================================================================
// Temporary log file.  Will be deleted if a reset or power failure occurs.
#define TMP_FILE_NAME "tmp_log.bin"

// Size of file base name.  Must not be larger than six.
const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;

// Number of analog pins to log.
const uint8_t PIN_COUNT = sizeof(PIN_LIST)/sizeof(PIN_LIST[0]);

// Minimum ADC clock cycles per sample interval
const uint16_t MIN_ADC_CYCLES = 15;

// Extra cpu cycles to setup ADC with more than one pin per sample.
const uint16_t ISR_SETUP_ADC = PIN_COUNT > 1 ? 100 : 0;

// Maximum cycles for timer0 system interrupt, millis, micros.
const uint16_t ISR_TIMER0 = 160;
//==============================================================================
SdFat sd;

SdBaseFile binFile;

char binName[13] = FILE_BASE_NAME "00.bin";

#if RECORD_EIGHT_BITS
const size_t SAMPLES_PER_BLOCK = DATA_DIM8/PIN_COUNT;
typedef block8_t block_t;
#else  // RECORD_EIGHT_BITS
const size_t SAMPLES_PER_BLOCK = DATA_DIM16/PIN_COUNT;
typedef block16_t block_t;
#endif // RECORD_EIGHT_BITS

block_t* emptyQueue[QUEUE_DIM];
uint8_t emptyHead;
uint8_t emptyTail;

block_t* fullQueue[QUEUE_DIM];
volatile uint8_t fullHead;  // volatile insures non-interrupt code sees changes.
uint8_t fullTail;

// queueNext assumes QUEUE_DIM is a power of two
inline uint8_t queueNext(uint8_t ht) {
  return (ht + 1) & (QUEUE_DIM -1);
}
//==============================================================================
// Interrupt Service Routines

// Pointer to current buffer.
block_t* isrBuf;

// Need new buffer if true.
bool isrBufNeeded = true;

// overrun count
uint16_t isrOver = 0;

// ADC configuration for each pin.
uint8_t adcmux[PIN_COUNT];
uint8_t adcsra[PIN_COUNT];
uint8_t adcsrb[PIN_COUNT];
uint8_t adcindex = 1;

// Insure no timer events are missed.
volatile bool timerError = false;
volatile bool timerFlag = false;
//------------------------------------------------------------------------------
// ADC done interrupt.
ISR(ADC_vect) {
  // Read ADC data.
#if RECORD_EIGHT_BITS
  uint8_t d = ADCH;
#else  // RECORD_EIGHT_BITS
  // This will access ADCL first.
  uint16_t d = ADC;
#endif  // RECORD_EIGHT_BITS

  if (isrBufNeeded && emptyHead == emptyTail) {
    // no buffers - count overrun
    if (isrOver < 0XFFFF) {
      isrOver++;
    }

    // Avoid missed timer error.
    timerFlag = false;
    return;
  }
  // Start ADC
  if (PIN_COUNT > 1) {
    ADMUX = adcmux[adcindex];
    ADCSRB = adcsrb[adcindex];
    ADCSRA = adcsra[adcindex];
    if (adcindex == 0) {
      timerFlag = false;
    }
    adcindex =  adcindex < (PIN_COUNT - 1) ? adcindex + 1 : 0;
  } else {
    timerFlag = false;
  }
  // Check for buffer needed.
  if (isrBufNeeded) {
    // Remove buffer from empty queue.
    isrBuf = emptyQueue[emptyTail];
    emptyTail = queueNext(emptyTail);
    isrBuf->count = 0;
    isrBuf->overrun = isrOver;
    isrBufNeeded = false;
  }
  // Store ADC data.
  isrBuf->data[isrBuf->count++] = d;

  // Check for buffer full.
  if (isrBuf->count >= PIN_COUNT*SAMPLES_PER_BLOCK) {
    // Put buffer isrIn full queue.
    uint8_t tmp = fullHead;  // Avoid extra fetch of volatile fullHead.
    fullQueue[tmp] = (block_t*)isrBuf;
    fullHead = queueNext(tmp);

    // Set buffer needed and clear overruns.
    isrBufNeeded = true;
    isrOver = 0;
  }
}
//------------------------------------------------------------------------------
// timer1 interrupt to clear OCF1B
ISR(TIMER1_COMPB_vect) {
  // Make sure ADC ISR responded to timer event.
  if (timerFlag) {
    timerError = true;
  }
  timerFlag = true;
}
//==============================================================================
// Error messages stored in flash.
#define error(msg) {sd.errorPrint(F(msg));fatalBlink();}
//------------------------------------------------------------------------------
//
void fatalBlink() {
  while (true) {
    if (ERROR_LED_PIN >= 0) {
      digitalWrite(ERROR_LED_PIN, HIGH);
      delay(200);
      digitalWrite(ERROR_LED_PIN, LOW);
      delay(200);
    }
  }
}
//==============================================================================
#if ADPS0 != 0 || ADPS1 != 1 || ADPS2 != 2
#error unexpected ADC prescaler bits
#endif
//------------------------------------------------------------------------------
// initialize ADC and timer1
void adcInit(metadata_t* meta) {
  uint8_t adps;  // prescaler bits for ADCSRA
  uint32_t ticks = F_CPU*SAMPLE_INTERVAL + 0.5;  // Sample interval cpu cycles.

  if (ADC_REF & ~((1 << REFS0) | (1 << REFS1))) {
    error("Invalid ADC reference");
  }
#ifdef ADC_PRESCALER
  if (ADC_PRESCALER > 7 || ADC_PRESCALER < 2) {
    error("Invalid ADC prescaler");
  }
  adps = ADC_PRESCALER;
#else  // ADC_PRESCALER
  // Allow extra cpu cycles to change ADC settings if more than one pin.
  int32_t adcCycles = (ticks - ISR_TIMER0)/PIN_COUNT - ISR_SETUP_ADC;

  for (adps = 7; adps > 0; adps--) {
    if (adcCycles >= (MIN_ADC_CYCLES << adps)) {
      break;
    }
  }
#endif  // ADC_PRESCALER
  meta->adcFrequency = F_CPU >> adps;
  if (meta->adcFrequency > (RECORD_EIGHT_BITS ? 2000000 : 1000000)) {
    error("Sample Rate Too High");
  }
#if ROUND_SAMPLE_INTERVAL
  // Round so interval is multiple of ADC clock.
  ticks += 1 << (adps - 1);
  ticks >>= adps;
  ticks <<= adps;
#endif  // ROUND_SAMPLE_INTERVAL

  if (PIN_COUNT > sizeof(meta->pinNumber)/sizeof(meta->pinNumber[0])) {
    error("Too many pins");
  }
  meta->pinCount = PIN_COUNT;
  meta->recordEightBits = RECORD_EIGHT_BITS;

  for (int i = 0; i < PIN_COUNT; i++) {
    uint8_t pin = PIN_LIST[i];
    if (pin >= NUM_ANALOG_INPUTS) {
      error("Invalid Analog pin number");
    }
    meta->pinNumber[i] = pin;

    // Set ADC reference and low three bits of analog pin number.
    adcmux[i] = (pin & 7) | ADC_REF;
    if (RECORD_EIGHT_BITS) {
      adcmux[i] |= 1 << ADLAR;
    }

    // If this is the first pin, trigger on timer/counter 1 compare match B.
    adcsrb[i] = i == 0 ? (1 << ADTS2) | (1 << ADTS0) : 0;
#ifdef MUX5
    if (pin > 7) {
      adcsrb[i] |= (1 << MUX5);
    }
#endif  // MUX5
    adcsra[i] = (1 << ADEN) | (1 << ADIE) | adps;
    adcsra[i] |= i == 0 ? 1 << ADATE : 1 << ADSC;
  }

  // Setup timer1
  TCCR1A = 0;
  uint8_t tshift;
  if (ticks < 0X10000) {
    // no prescale, CTC mode
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);
    tshift = 0;
  } else if (ticks < 0X10000*8) {
    // prescale 8, CTC mode
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);
    tshift = 3;
  } else if (ticks < 0X10000*64) {
    // prescale 64, CTC mode
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11) | (1 << CS10);
    tshift = 6;
  } else if (ticks < 0X10000*256) {
    // prescale 256, CTC mode
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12);
    tshift = 8;
  } else if (ticks < 0X10000*1024) {
    // prescale 1024, CTC mode
    TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12) | (1 << CS10);
    tshift = 10;
  } else {
    error("Sample Rate Too Slow");
  }
  // divide by prescaler
  ticks >>= tshift;
  // set TOP for timer reset
  ICR1 = ticks - 1;
  // compare for ADC start
  OCR1B = 0;

  // multiply by prescaler
  ticks <<= tshift;

  // Sample interval in CPU clock ticks.
  meta->sampleInterval = ticks;
  meta->cpuFrequency = F_CPU;
  float sampleRate = (float)meta->cpuFrequency/meta->sampleInterval;
  Serial.print(F("Sample pins:"));
  for (uint8_t i = 0; i < meta->pinCount; i++) {
    Serial.print(' ');
    Serial.print(meta->pinNumber[i], DEC);
  }
  Serial.println();
  Serial.print(F("ADC bits: "));
  Serial.println(meta->recordEightBits ? 8 : 10);
  Serial.print(F("ADC clock kHz: "));
  Serial.println(meta->adcFrequency/1000);
  Serial.print(F("Sample Rate: "));
  Serial.println(sampleRate);
  Serial.print(F("Sample interval usec: "));
  Serial.println(1000000.0/sampleRate, 4);
}
//------------------------------------------------------------------------------
// enable ADC and timer1 interrupts
void adcStart() {
  // initialize ISR
  isrBufNeeded = true;
  isrOver = 0;
  adcindex = 1;

  // Clear any pending interrupt.
  ADCSRA |= 1 << ADIF;

  // Setup for first pin.
  ADMUX = adcmux[0];
  ADCSRB = adcsrb[0];
  ADCSRA = adcsra[0];

  // Enable timer1 interrupts.
  timerError = false;
  timerFlag = false;
  TCNT1 = 0;
  TIFR1 = 1 << OCF1B;
  TIMSK1 = 1 << OCIE1B;
}
//------------------------------------------------------------------------------
void adcStop() {
  TIMSK1 = 0;
  ADCSRA = 0;
}
//------------------------------------------------------------------------------
// Convert binary file to csv file.
void binaryToCsv() {
  uint8_t lastPct = 0;
  block_t buf;
  metadata_t* pm;
  uint32_t t0 = millis();
  char csvName[13];
  StdioStream csvStream;

  if (!binFile.isOpen()) {
    Serial.println(F("No current binary file"));
    return;
  }
  binFile.rewind();
  if (binFile.read(&buf , 512) != 512) {
    error("Read metadata failed");
  }
  // Create a new csv file.
  strcpy(csvName, binName);
  strcpy(&csvName[BASE_NAME_SIZE + 3], "csv");

  if (!csvStream.fopen(csvName, "w")) {
    error("open csvStream failed");
  }
  Serial.println();
  Serial.print(F("Writing: "));
  Serial.print(csvName);
  Serial.println(F(" - type any character to stop"));
  pm = (metadata_t*)&buf;
  csvStream.print(F("Interval,"));
  float intervalMicros = 1.0e6*pm->sampleInterval/(float)pm->cpuFrequency;
  csvStream.print(intervalMicros, 4);
  csvStream.println(F(",usec"));
  for (uint8_t i = 0; i < pm->pinCount; i++) {
    if (i) {
      csvStream.putc(',');
    }
    csvStream.print(F("pin"));
    csvStream.print(pm->pinNumber[i]);
  }
  csvStream.println();
  uint32_t tPct = millis();
  while (!Serial.available() && binFile.read(&buf, 512) == 512) {
    if (buf.count == 0) {
      break;
    }
    if (buf.overrun) {
      csvStream.print(F("OVERRUN,"));
      csvStream.println(buf.overrun);
    }
    for (uint16_t j = 0; j < buf.count; j += PIN_COUNT) {
      for (uint16_t i = 0; i < PIN_COUNT; i++) {
        if (i) {
          csvStream.putc(',');
        }
        csvStream.print(buf.data[i + j]);
      }
      csvStream.println();
    }
    if ((millis() - tPct) > 1000) {
      uint8_t pct = binFile.curPosition()/(binFile.fileSize()/100);
      if (pct != lastPct) {
        tPct = millis();
        lastPct = pct;
        Serial.print(pct, DEC);
        Serial.println('%');
      }
    }
    if (Serial.available()) {
      break;
    }
  }
  csvStream.fclose();
  Serial.print(F("Done: "));
  Serial.print(0.001*(millis() - t0));
  Serial.println(F(" Seconds"));
}
//------------------------------------------------------------------------------
// read data file and check for overruns
void checkOverrun() {
  bool headerPrinted = false;
  block_t buf;
  uint32_t bgnBlock, endBlock;
  uint32_t bn = 0;

  if (!binFile.isOpen()) {
    Serial.println(F("No current binary file"));
    return;
  }
  if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
    error("contiguousRange failed");
  }
  binFile.rewind();
  Serial.println();
  Serial.println(F("Checking overrun errors - type any character to stop"));
  if (binFile.read(&buf , 512) != 512) {
    error("Read metadata failed");
  }
  bn++;
  while (binFile.read(&buf, 512) == 512) {
    if (buf.count == 0) {
      break;
    }
    if (buf.overrun) {
      if (!headerPrinted) {
        Serial.println();
        Serial.println(F("Overruns:"));
        Serial.println(F("fileBlockNumber,sdBlockNumber,overrunCount"));
        headerPrinted = true;
      }
      Serial.print(bn);
      Serial.print(',');
      Serial.print(bgnBlock + bn);
      Serial.print(',');
      Serial.println(buf.overrun);
    }
    bn++;
  }
  if (!headerPrinted) {
    Serial.println(F("No errors found"));
  } else {
    Serial.println(F("Done"));
  }
}
//------------------------------------------------------------------------------
// dump data file to Serial
void dumpData() {
  block_t buf;
  if (!binFile.isOpen()) {
    Serial.println(F("No current binary file"));
    return;
  }
  binFile.rewind();
  if (binFile.read(&buf , 512) != 512) {
    error("Read metadata failed");
  }
  Serial.println();
  Serial.println(F("Type any character to stop"));
  delay(1000);
  while (!Serial.available() && binFile.read(&buf , 512) == 512) {
    if (buf.count == 0) {
      break;
    }
    if (buf.overrun) {
      Serial.print(F("OVERRUN,"));
      Serial.println(buf.overrun);
    }
    for (uint16_t i = 0; i < buf.count; i++) {
      Serial.print(buf.data[i], DEC);
      if ((i+1)%PIN_COUNT) {
        Serial.print(',');
      } else {
        Serial.println();
      }
    }
  }
  Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
// log data
// max number of blocks to erase per erase call
uint32_t const ERASE_SIZE = 262144L;
void logData() {
  uint32_t bgnBlock, endBlock;

  // Allocate extra buffer space.
  block_t block[BUFFER_BLOCK_COUNT];

  Serial.println();

  // Initialize ADC and timer1.
  adcInit((metadata_t*) &block[0]);

  // Find unused file name.
  if (BASE_NAME_SIZE > 6) {
    error("FILE_BASE_NAME too long");
  }
  while (sd.exists(binName)) {
    if (binName[BASE_NAME_SIZE + 1] != '9') {
      binName[BASE_NAME_SIZE + 1]++;
    } else {
      binName[BASE_NAME_SIZE + 1] = '0';
      if (binName[BASE_NAME_SIZE] == '9') {
        error("Can't create file name");
      }
      binName[BASE_NAME_SIZE]++;
    }
  }
  // Delete old tmp file.
  if (sd.exists(TMP_FILE_NAME)) {
    Serial.println(F("Deleting tmp file"));
    if (!sd.remove(TMP_FILE_NAME)) {
      error("Can't remove tmp file");
    }
  }
  // Create new file.
  Serial.println(F("Creating new file"));
  binFile.close();
  if (!binFile.createContiguous(TMP_FILE_NAME, 512 * FILE_BLOCK_COUNT)) {
    error("createContiguous failed");
  }
  // Get the address of the file on the SD.
  if (!binFile.contiguousRange(&bgnBlock, &endBlock)) {
    error("contiguousRange failed");
  }
  // Use SdFat's internal buffer.
  uint8_t* cache = (uint8_t*)sd.vol()->cacheClear();
  if (cache == 0) {
    error("cacheClear failed");
  }

  // Flash erase all data in the file.
  Serial.println(F("Erasing all data"));
  uint32_t bgnErase = bgnBlock;
  uint32_t endErase;
  while (bgnErase < endBlock) {
    endErase = bgnErase + ERASE_SIZE;
    if (endErase > endBlock) {
      endErase = endBlock;
    }
    if (!sd.card()->erase(bgnErase, endErase)) {
      error("erase failed");
    }
    bgnErase = endErase + 1;
  }
  // Start a multiple block write.
  if (!sd.card()->writeStart(bgnBlock, FILE_BLOCK_COUNT)) {
    error("writeBegin failed");
  }
  // Write metadata.
  if (!sd.card()->writeData((uint8_t*)&block[0])) {
    error("Write metadata failed");
  }
  // Initialize queues.
  emptyHead = emptyTail = 0;
  fullHead = fullTail = 0;

  // Use SdFat buffer for one block.
  emptyQueue[emptyHead] = (block_t*)cache;
  emptyHead = queueNext(emptyHead);

  // Put rest of buffers in the empty queue.
  for (uint8_t i = 0; i < BUFFER_BLOCK_COUNT; i++) {
    emptyQueue[emptyHead] = &block[i];
    emptyHead = queueNext(emptyHead);
  }
  // Give SD time to prepare for big write.
  delay(1000);
  Serial.println(F("Logging - type any character to stop"));
  // Wait for Serial Idle.
  Serial.flush();
  delay(10);
  uint32_t bn = 1;
  uint32_t t0 = millis();
  uint32_t t1 = t0;
  uint32_t overruns = 0;
  uint32_t count = 0;
  uint32_t maxLatency = 0;

  // Start logging interrupts.
  adcStart();
  while (1) {
    if (fullHead != fullTail) {
      // Get address of block to write.
      block_t* pBlock = fullQueue[fullTail];

      // Write block to SD.
      uint32_t usec = micros();
      if (!sd.card()->writeData((uint8_t*)pBlock)) {
        error("write data failed");
      }
      usec = micros() - usec;
      t1 = millis();
      if (usec > maxLatency) {
        maxLatency = usec;
      }
      count += pBlock->count;

      // Add overruns and possibly light LED.
      if (pBlock->overrun) {
        overruns += pBlock->overrun;
        if (ERROR_LED_PIN >= 0) {
          digitalWrite(ERROR_LED_PIN, HIGH);
        }
      }
      // Move block to empty queue.
      emptyQueue[emptyHead] = pBlock;
      emptyHead = queueNext(emptyHead);
      fullTail = queueNext(fullTail);
      bn++;
      if (bn == FILE_BLOCK_COUNT) {
        // File full so stop ISR calls.
        adcStop();
        break;
      }
    }
    if (timerError) {
      error("Missed timer event - rate too high");
    }
    if (Serial.available()) {
      // Stop ISR calls.
      adcStop();
      if (isrBuf != 0 && isrBuf->count >= PIN_COUNT) {
        // Truncate to last complete sample.
        isrBuf->count = PIN_COUNT*(isrBuf->count/PIN_COUNT);
        // Put buffer in full queue.
        fullQueue[fullHead] = isrBuf;
        fullHead = queueNext(fullHead);
        isrBuf = 0;
      }
      if (fullHead == fullTail) {
        break;
      }
    }
  }
  if (!sd.card()->writeStop()) {
    error("writeStop failed");
  }
  // Truncate file if recording stopped early.
  if (bn != FILE_BLOCK_COUNT) {
    Serial.println(F("Truncating file"));
    if (!binFile.truncate(512L * bn)) {
      error("Can't truncate file");
    }
  }
  if (!binFile.rename(binName)) {
    error("Can't rename file");
  }
  Serial.print(F("File renamed: "));
  Serial.println(binName);
  Serial.print(F("Max block write usec: "));
  Serial.println(maxLatency);
  Serial.print(F("Record time sec: "));
  Serial.println(0.001*(t1 - t0), 3);
  Serial.print(F("Sample count: "));
  Serial.println(count/PIN_COUNT);
  Serial.print(F("Samples/sec: "));
  Serial.println((1000.0/PIN_COUNT)*count/(t1-t0));
  Serial.print(F("Overruns: "));
  Serial.println(overruns);
  Serial.println(F("Done"));
}
//------------------------------------------------------------------------------
void setup(void) {
  if (ERROR_LED_PIN >= 0) {
    pinMode(ERROR_LED_PIN, OUTPUT);
  }
  Serial.begin(115200);

  // Read the first sample pin to init the ADC.
  analogRead(PIN_LIST[0]);

  Serial.print(F("FreeStack: "));
  Serial.println(FreeStack());

  // Initialize at the highest speed supported by the board that is
  // not over 50 MHz. Try a lower speed if SPI errors occur.
  if (!sd.begin(SD_CS_PIN, SD_SCK_MHZ(50))) {
    sd.initErrorPrint();
    fatalBlink();
  }
}
//------------------------------------------------------------------------------
void loop(void) {
  // Read any Serial data.
  do {
    delay(10);
  } while (Serial.available() && Serial.read() >= 0);
  Serial.println();
  Serial.println(F("type:"));
  Serial.println(F("c - convert file to csv"));
  Serial.println(F("d - dump data to Serial"));
  Serial.println(F("e - overrun error details"));
  Serial.println(F("r - record ADC data"));

  while(!Serial.available()) {
    SysCall::yield();
  }
  char c = tolower(Serial.read());
  if (ERROR_LED_PIN >= 0) {
    digitalWrite(ERROR_LED_PIN, LOW);
  }
  // Read any Serial data.
  do {
    delay(10);
  } while (Serial.available() && Serial.read() >= 0);

  if (c == 'c') {
    binaryToCsv();
  } else if (c == 'd') {
    dumpData();
  } else if (c == 'e') {
    checkOverrun();
  } else if (c == 'r') {
    logData();
  } else {
    Serial.println(F("Invalid entry"));
  }
}
#else  // __AVR__
#error This program is only for AVR.
#endif  // __AVR__

why don't you try it and find out?

How are you achieving the 5kHz? If you are using something like a timer, you should get a pretty solid 5kHz (the only drift would be the drift of the crystal). In that case not useful to add a timestamp to the data, as it would be a constant 200µs after the previous one. Though you'd have to look out for other interrupts, which can cause jitter. You also could run into problems when a SD cards write take a bit longer than usual. In that case you could lose some of your measurements. Adding a timestamp would at least enable you to detect that.


text:
Arduino boards are not working with powerbanks!
I have used many Arduino boards with adapters and other power supplies. While connecting with a powerbank for portable projects I have noticed that the power goes off after a couple of seconds and then turn on again after sometimes. Why this is happening with power bank?
I have used following powerbanks:

Basus
Xiaomi

refer to the powerbank datasheet for minimum and maximum allowable load

Most USB powerbanks will automatically turn off if there is not a minimum current draw to keep them turned on.  The Arduino is obviously drawing current below the limit for the devices you have.
A couple of options:

Add some additional load to keep the powerbank on indefinitely.  Of course this may waste power but that's part of the price to pay.
Find a power bank that is less sophisticated.  I have one with a manual on/off switch.  It powers the Arduino just fine until it runs out of power.
Find a different powerbank that has a lower threshold for staying on.  Some vendors give you this spec and others don't.
Modify the powerbank to stay on.  I'm not sure there is a general way to do this but you might experiment with it and see.

Any links to indicate power banks automatically turn off below a certain current or power threshold after a certain time would be useful, even if they are just links to user manuals you are familiar with.

I thought it was a working method of powerbank . Thanks for the suggestion.

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes


text:
Record data to SD card with ATtiny85
An application of ATtiny with SD card is complicated due to missing of hardware SPI in attiny.
The "ATmega" code for creating of a "SD file" is:
#include <SPI.h>
#include <SD.h>

File file;
int const pinCS = 2;

void setup()

{
SD.begin(pinCS);
file = SD.open("Test.txt", FILE_WRITE);
file.println("anything");
file.close();}

void loop()
{}

This code creates on SD card a file "Test.txt" and to this file records text "anathing".
Is it possible to rewrite this code for "ATtiny85"?

The ATtiny85 has no dedicated SPI port. It has, however, an “USI” port
(Universal Serial Interface) that should be able to do the job. The lack
of SPI should not be a problem, but the lack of memory will be a
problem.
Files on an SD card are written one sector (512 bytes) at a time.
For this reason, the SD library uses a 512 bytes buffer in RAM.
That cannot work on an ATtiny85, as the buffer would consume the whole
RAM.
You may be able to do something using the Petit FAT library,
provided you can work around the severe limitations of its write
interface:

The write function has some restrictions listed below:

Cannot create file. Only existing file can be written.
Cannot expand file size.
Cannot update time stamp of the file.
Write operation can start/stop on the sector boundary only.
Read-only attribute of the file cannot block write operation.

The first two points imply that you would have to create the file and
allocate the sectors for it in your computer.
This blog post mentions a port of the library to the Arduino
environment, and its use on an ATtiny84, which has similar capabilities
to the ATtiny85 (only with more pins).

Thank you of this relevant response. No problem, ATmega operates good in this connection, but ....attiny is smaller and sympatric. It is pity, the limitations of the "Petit.." is problem. Thank You.

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
Why do I get error "Error compiling for board Arduino Uno" with my mpu6050 video stabilization code?
This is the code I'm working with. I'm using it for video stabilization system. It was working fine but for some reason now it gives me this error? Is it because I deleted the library and installed again? Please help. I'm new to arduino. Hopefully I formatted this correctly.
#include <Servo.h>
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif

MPU6050 mpu;
Servo myServo;   // define servo
int servoPin=9;
float PWM = 0;
float Yaw = 0;

#define OUTPUT_READABLE_YAWPITCHROLL
#define INTERRUPT_PIN 2
#define LED_PIN 13

bool blinkState = false;

// MPU control/status vars
bool dmpReady=false;    // set true if DMP init was successful
uint8_t mpuIntStatus;   // holds actual interrupt status byte from MPU
uint8_t devStatus;      // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize;    // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount;     // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer

// orientation/motion vars
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float euler[3];         // [psi, theta, phi]    Euler angle container
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
//packet structure for InvenSense teapot demo

volatile bool mpuInterrupt=false;
void dmpDataReady(){
  mpuInterrupt=true;
}
// ================================================================
// ===                      INITIAL SETUP                       ===
// ================================================================
void setup()
{
  // join I2C bus (I2Cdev library doesn't do this automatically)
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
  Wire.begin();
  Wire.setClock(400000);

#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
  Fastwire::setup(400, true);

#endif

  Serial.begin(115200);
  while (!Serial);      // wait for Leonardo enumeration, others continue immediately

  // initialize device
  Serial.println(F("Initializing I2C devices..."));
  pinMode(INTERRUPT_PIN, INPUT);
  // testing connection...
  Serial.println(F("Testing device connections..."));
  Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
  // load and configure the DMP
  Serial.println(F("Initializing DMP"));
  devStatus = mpu.dmpInitialize();

  // INPUT CALIBRATED OFFSETS HERE; SPECIFIC FOR EACH UNIT AND EACH MOUNTING CONFIGURATION!!!!
  //fix offsets #s when we use mount
  mpu.setXGyroOffset(0);
  mpu.setYGyroOffset(0);
  mpu.setZGyroOffset(0);
  //mpu.setXAccelOffset(-651); //unneccesary
  //mpu.setYAccelOffset(670);  //also unneccesary
  mpu.setZAccelOffset(17);

  // make sure it worked (returns 0 if so)
  if (devStatus == 0){
    // Calibration Time: generate offsets and calibrate our MPU6050
     // mpu.CalibrateAccel(6);
     // mpu.CalibrateGyro(6);
     // mpu.PrintActiveOffsets();
    // turn on the DMP, now that it's ready
       Serial.println(F("Enabling DMP..."));
        mpu.setDMPEnabled(true);

    // enable Arduino interrupt detection
    Serial.print(F("Enabling interrupt detection (Arduino external interrupt 0)"));
    Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
    Serial.println(F(")..."));
    attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);

    mpuIntStatus = mpu.getIntStatus();
    Serial.println(F("DMP ready! Waiting for first interrupt..."));
        dmpReady = true;
    // get expected DMP packet size for later comparison
    packetSize = mpu.dmpGetFIFOPacketSize();
  }else
  {
    // ERROR!
    // 1 = initial memory load failed, 2 = DMP configuration updates failed (if it's going to break, usually the code will be 1)
    Serial.print(F("DMP Initialization failed code = "));
    Serial.println(devStatus);
    Serial.println(F(")"));
  }

  // configure LED for output
  pinMode(LED_PIN, OUTPUT);
  myServo.attach(servoPin);
  PWM=0;
}

//main program loop

void loop()
{
  // if programming failed, don't try to do anything
    if (!dmpReady) return;
    // wait for MPU interrupt or extra packet(s) available
    while (!mpuInterrupt && fifoCount < packetSize) {
        if (mpuInterrupt && fifoCount < packetSize) {
          // try to get out of the infinite loop
          fifoCount = mpu.getFIFOCount();
        }
        // other program behavior stuff here
    }

  //reset inerrupt flag Get INT_STATUS byte
  mpuInterrupt=false;
  mpuIntStatus = mpu.getIntStatus();

  // get current FIFO count
  fifoCount = mpu.getFIFOCount();

  // check for overflow (this should never happen unless our code is too inefficient)
  if (fifoCount<packetSize){

    // reset so we can continue cleanly
  }else if ((mpuIntStatus & _BV(MPU6050_INTERRUPT_FIFO_OFLOW_BIT)) || fifoCount >= 1024) {

    mpu.resetFIFO();
    Serial.println(F("FIFO overflow!"));

  }else if (mpuIntStatus & _BV (MPU6050_INTERRUPT_DMP_INT_BIT)){

   while (fifoCount>=packetSize)
  {
    // check for correct available data length
    //if (fifoCount < packetSize)
    //return; //  fifoCount = mpu.getFIFOCount();

    // read a packet from FIFO
    mpu.getFIFOBytes(fifoBuffer, packetSize);

    // track FIFO count here in case there is > 1 packet available
    fifoCount -= packetSize;
   }
     #ifdef OUTPUT_READABLE_QUATERNION
     //display quaternion values in easty matrix form: w x y x
    mpu.dmpGetQuaternion(&q, fifoBuffer);
            Serial.print("quat\t");
            Serial.print(q.w);
            Serial.print("\t");
            Serial.print(q.x);
            Serial.print("\t");
            Serial.print(q.y);
            Serial.print("\t");
            Serial.print(q.z);

    #endif
    #ifdef OUTPUT_READABLE_EULER
            // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetEuler(euler, &q);
            Serial.print("euler\t");
            Serial.print(euler[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(euler[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(euler[2] * 180/M_PI);
        #endif
        #ifdef OUTPUT_READABLE_YAWPITCHROLL
            // display Euler angles in degrees
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
            Yaw = ypr[0]*180/M_PI;
            Serial.print("ypr\t");
            Serial.print(ypr[0] * 180/M_PI);
            Serial.print("\t");
            Serial.print(ypr[1] * 180/M_PI);
            Serial.print("\t");
            Serial.println(ypr[2] * 180/M_PI);
        #endif
        #ifdef OUTPUT_READABLE_REALACCEL
            // display real acceleration, adjusted to remove gravity
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetAccel(&aa, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
            Serial.print("areal\t");
            Serial.print(aaReal.x);
            Serial.print("\t");
            Serial.print(aaReal.y);
            Serial.print("\t");
            Serial.println(aaReal.z);
        #endif
        #ifdef OUTPUT_READABLE_WORLDACCEL
            mpu.dmpGetQuaternion(&q, fifoBuffer);
            mpu.dmpGetAccel(&aa, fifoBuffer);
            mpu.dmpGetGravity(&gravity, &q);
            mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
            mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
            Serial.print("aworld\t");
            Serial.print(aaWorld.x);
            Serial.print("\t");
            Serial.print(aaWorld.y);
            Serial.print("\t");
            Serial.println(aaWorld.z);
        #endif

    // blink LED to indicate activity
    blinkState = !blinkState;
    digitalWrite(LED_PIN, blinkState);
  }
  PWM= map(Yaw, -180, 180, 1860, 1140);
  myServo.write(PWM);
  Serial.println(PWM);
  Serial.println(Yaw);
}

If the error code is needed please let me know. It's too long to attach in the body

Only the first one perhaps two errors in compiler output are normally useful.  The ones that follow are often the result of the first error or the compiler's attempt to recover from it and sometimes end up being more misleading than helpful.  It's probably enough to paste just the top of the output.

Thanks for responding. I had multiples of some libraries. Don't know how that happened. But I uninstalled all the libraries, and manually installed the ones I needed. It works now.


text:
Send commands from cPanel website to client ESP8266
I have a project where I want to remotely turn on and off a relay using an Arduino,(ESP8266) over the internet.

I have a remote web server that hosts my existing hobby website, cPanel hosted by GoDaddy Sites.
An Arduino (ESP8266) connects to this website listening for relay on/off commands.
Open a browser and load the webpage dashboard to sent commands to Arduino.

Yes I could have the Arduino poll the website ever so often to check for a value changed in a database, but I don't want to do that, Just seems unethical to me.
I was looking at WebSockets and thought that might be what I'm looking for but I know too little about it or there's not enough relevant documentation online about it to use it in this type of project.
I am looking forward to your comments and answers, but please do not tell me to host the site on the Arduino, and also using an app or other service is not going to work either, We are working with my own webpage and thats it no thirdparty apps, Thanks.

What is the question?

why is checking a website unethical?

Because it is like a little kid in the back seat asking every km on the road, "are we there yet"? If there could be a way to tell the client when to turn on a relay only when needed, would be much more efficient. there will be millions of checks that are essentially useless, so having it just wait until the command is sent to it would be ideal.

I don't believe you will have the capability to implement a Websocket interface on the GoDaddy cPanel web site.  I think you are limited to just serving up HTML content and associated files.  Websocket requires a server-side resource to talk to which you cannot implement.
I see nothing wrong with having a simple HTML page, that doesn't even need to really contain HTML that your ESP8266 can "GET" and examine and determine to turn your relay on or off.

Thanks, yes that would and will probably work, as per my previous comment, it's just not ideal that way, creates constant internet traffic that's essentially useless.


text:
Multitasking with Arduino?
I am making a Christmas Tree with Arduino. I found a 12 led chaser code on internet so I made 12 rows of leds and hook them up to my Arduino pro mini.
They are working just fine.
However, I also want to add music with piezo buzzer while running the LEDs. Is that possible somehow or should I use another microcontroller for piezo? I also used pin 10 for piezo buzzer and used pin 14(a0) for led assigned for pin 10 previously..
Here are the codes:
12 led chaser
#define t   30
#define t1  20
#define t2  100
#define t3  50

void setup() {
  // set up pins 2 to 13 as outputs
  for (int i = 2; i <= 13; i++) {
    pinMode(i, OUTPUT);
  }
}
/////////////////////////////////////////////////////////////////////////////////Effect 1
void loop(){

effect_1();
effect_1();

 effect_2();
 effect_2();

 effect_3();
 effect_3();

 effect_4();
 effect_4();

 effect_5();
 effect_5();

 effect_6();
 effect_6();

 effect_7();
 effect_7();
  }
//left to right and right to left
void effect_1()
{
for(int i=2; i<14; i++){
digitalWrite(i, HIGH);
delay(t1);
digitalWrite(i+1, HIGH);
delay(t1);
digitalWrite(i+2, HIGH);
delay(t1);
digitalWrite(i, LOW);
delay(t1);
digitalWrite(i+1, LOW);
delay(t1);
}
for(int i=13; i>1; i--){
digitalWrite(i, HIGH);
delay(t1);
digitalWrite(i-1, HIGH);
delay(t1);
digitalWrite(i-2, HIGH);
delay(t1);
digitalWrite(i, LOW);
delay(t1);
digitalWrite(i-1, LOW);
delay(t1);
}
}
/////////////////////////////////////////////////////////////////////////////////Effect 2
void effect_2()
{
int count = 13; // keeps track of second LED movement

  // move first LED from left to right and second from right to left
  for (int i = 2; i < 13; i++) {
    clear();
    digitalWrite(i, HIGH);      // chaser 1
    digitalWrite(count, HIGH); // chaser 2
    count--;
    // stop LEDs from appearing to stand still in the middle
    if (count != 7) {
      delay(t2);
    }
  }

  // move first LED from right to left and second LED from left to right
  for (int i = 13; i > 2; i--) {
    clear();
    digitalWrite(i, HIGH);      // chaser 1
    digitalWrite(count, HIGH); // chaser 2
    count++;
    // stop LEDs from appearing to stand still in the middle
    if (count != 8) {
      delay(t2);
    }
  }
}

////////////////////////////////////////////////////////////////////////////////Effect 3

void effect_3()
{
for(int i=2; i<14; i++){
  digitalWrite(i, HIGH);
  delay(t3);
}
for(int i=2; i<14; i++){
  digitalWrite(i, LOW);
  delay(t3);
}

for(int i = 14; i>=2; i--){
  digitalWrite(i, HIGH);
  delay(t3);
}
for(int i = 14; i>=2; i--){
  digitalWrite(i, LOW);
  delay(t3);
}
}
  ///////////////////////////////////////////////////////////////////////////Effect 4
void effect_4()
{
for(int j = 2; j <= 13; j++){
  digitalWrite(j, HIGH);
  delay(t2);
  j=j+1;
  }
for(int j = 2; j <= 13; j++){
  digitalWrite(j, LOW);
  delay(t2);
  }

  for(int k = 15; k > 2; k--){
  digitalWrite(k, HIGH);
  delay(t2);
  k=k-1;
  }
for(int k = 15; k > 2; k--){
  digitalWrite(k, LOW);
  delay(t2);
  }
}
//////////////////////////////////////////////////////////////////////////////Effect 5
  void effect_5()
{
 for(int pin = 13; pin >= 2; pin--)
  {
  digitalWrite(pin, HIGH);
  delay(t1);
  digitalWrite(pin+1, LOW);
  delay(t1);
  }
  for(int pin = 13; pin >= 2; pin--)
  {
  digitalWrite(pin+1, HIGH);
  delay(t1);
  digitalWrite(pin+2, LOW);
  delay(t1);
  }
  for(int pin = 13; pin >= 2; pin--)
  {
  digitalWrite(pin+2, HIGH);
  delay(t1);
  digitalWrite(pin+3, LOW);
  delay(t1);
  }
  for(int pin = 13; pin >= 2; pin--)
  {
  digitalWrite(pin+3, HIGH);
  delay(t1);
  digitalWrite(pin+4, LOW);
  delay(t1);
  }
    for(int pin = 13; pin >= 2; pin--)
  {
  digitalWrite(pin+4, HIGH);
  delay(t1);
  digitalWrite(pin+5, LOW);
  delay(t1);
  }
  for(int pin = 13; pin >= 2; pin--)
  {
  digitalWrite(pin+5, HIGH);
  delay(t1);
  digitalWrite(pin+6, LOW);
  delay(t1);
  }
  for(int pin = 13; pin >= 2; pin--)
  {
  digitalWrite(pin+6, HIGH);
  delay(t1);
  digitalWrite(pin+7, LOW);
  delay(t1);
  }
   for(int pin = 13; pin >= 2; pin--)
  {
  digitalWrite(pin+7, HIGH);
  delay(t1);
  digitalWrite(pin+8, LOW);
  delay(t1);
  }
  for(int pin = 9; pin >= 2; pin--)
  {
  digitalWrite(pin+8, HIGH);
  delay(t1);
  digitalWrite(pin+9, LOW);
  delay(t1);
  }
    for(int pin = 9; pin >= 2; pin--)
  {
  digitalWrite(pin+9, HIGH);
  delay(t1);
  digitalWrite(pin+10, LOW);
  delay(t1);
  }
  for(int pin = 9; pin >= 2; pin--)
  {
  digitalWrite(pin+10, HIGH);
  delay(t1);
  digitalWrite(pin+11, LOW);
  delay(t1);
  }
  for(int pin = 14; pin >= 2; pin--)
  {
  digitalWrite(pin+11, HIGH);
  delay(t1);
  }
  for(int pin = 13; pin >= 2; pin--)
  {
    digitalWrite(pin, LOW);
  delay(t1);
    }
  }
  /////////////////////////////////////////////////////////////////////////////Effect 6
void effect_6()
{
for(int j=2; j<14; j++){
  digitalWrite(j, LOW);
  delay(t);
  digitalWrite(j, HIGH);
  delay(t);
  digitalWrite(j-2, LOW);
  delay(t);
  digitalWrite(j, HIGH);
}
for(int k = 15; k>2; k--){
  digitalWrite(k, LOW);
  delay(t);
  digitalWrite(k, HIGH);
  delay(t);
  digitalWrite(k+2, LOW);
  delay(t);
  digitalWrite(k, HIGH);
}
for(int k = 2; k<14; k++){
  digitalWrite(k, LOW);
  delay(t);
  digitalWrite(k, HIGH);
  delay(t);
  digitalWrite(k-2, LOW);
  delay(t);
  digitalWrite(k, HIGH);
}
for(int k = 15; k>2; k--){
  digitalWrite(k, LOW);
  delay(t);
  digitalWrite(k, HIGH);
  delay(t);
  digitalWrite(k+4, LOW);
  delay(t);
  digitalWrite(k, HIGH);
}
for(int k = 2; k<14; k++){
  digitalWrite(k, LOW);
  delay(t);
  digitalWrite(k, HIGH);
  delay(t);
  digitalWrite(k-4, LOW);
  delay(t);
  digitalWrite(k, HIGH);
}
}
///////////////////////////////////////////////////////////////////////////////////Effect 7
void effect_7()
{
for(int j=2; j<14; j++){
  digitalWrite(j, HIGH);
  delay(t);
  digitalWrite(j+2, LOW);
  delay(t);
}
for(int k = 15; k>2; k--){
  digitalWrite(k, HIGH);
  delay(t);
  digitalWrite(k+2, LOW);
  delay(t);
}
}

// function to switch all LEDs off
void clear(void)
{
  for (int i = 2; i <= 13; i++) {
    digitalWrite(i, LOW);
  }
}

Melody tone for buzzer:
/*
  We Wish You a Merry Christmas - Traditional Christmas song
  Connect a piezo buzzer or speaker to pin 11 or select a new pin.
  More songs available at https://github.com/robsoncouto/arduino-songs

                                              Robson Couto, 2019
*/
#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
#define REST      0

// change this to make the song slower or faster
int tempo = 140;

// change this to whichever pin you want to use
int buzzer = 11;

// notes of the moledy followed by the duration.
// a 4 means a quarter note, 8 an eighteenth , 16 sixteenth, so on
// !!negative numbers are used to represent dotted notes,
// so -4 means a dotted quarter note, that is, a quarter plus an eighteenth!!
int melody[] = {

  // We Wish You a Merry Christmas
  // Score available at https://musescore.com/user/6208766/scores/1497501

  NOTE_C5,4, //1
  NOTE_F5,4, NOTE_F5,8, NOTE_G5,8, NOTE_F5,8, NOTE_E5,8,
  NOTE_D5,4, NOTE_D5,4, NOTE_D5,4,
  NOTE_G5,4, NOTE_G5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F5,8,
  NOTE_E5,4, NOTE_C5,4, NOTE_C5,4,
  NOTE_A5,4, NOTE_A5,8, NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8,
  NOTE_F5,4, NOTE_D5,4, NOTE_C5,8, NOTE_C5,8,
  NOTE_D5,4, NOTE_G5,4, NOTE_E5,4,

  NOTE_F5,2, NOTE_C5,4, //8
  NOTE_F5,4, NOTE_F5,8, NOTE_G5,8, NOTE_F5,8, NOTE_E5,8,
  NOTE_D5,4, NOTE_D5,4, NOTE_D5,4,
  NOTE_G5,4, NOTE_G5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F5,8,
  NOTE_E5,4, NOTE_C5,4, NOTE_C5,4,
  NOTE_A5,4, NOTE_A5,8, NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8,
  NOTE_F5,4, NOTE_D5,4, NOTE_C5,8, NOTE_C5,8,
  NOTE_D5,4, NOTE_G5,4, NOTE_E5,4,
  NOTE_F5,2, NOTE_C5,4,

  NOTE_F5,4, NOTE_F5,4, NOTE_F5,4,//17
  NOTE_E5,2, NOTE_E5,4,
  NOTE_F5,4, NOTE_E5,4, NOTE_D5,4,
  NOTE_C5,2, NOTE_A5,4,
  NOTE_AS5,4, NOTE_A5,4, NOTE_G5,4,
  NOTE_C6,4, NOTE_C5,4, NOTE_C5,8, NOTE_C5,8,
  NOTE_D5,4, NOTE_G5,4, NOTE_E5,4,
  NOTE_F5,2, NOTE_C5,4,
  NOTE_F5,4, NOTE_F5,8, NOTE_G5,8, NOTE_F5,8, NOTE_E5,8,
  NOTE_D5,4, NOTE_D5,4, NOTE_D5,4,

  NOTE_G5,4, NOTE_G5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F5,8, //27
  NOTE_E5,4, NOTE_C5,4, NOTE_C5,4,
  NOTE_A5,4, NOTE_A5,8, NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8,
  NOTE_F5,4, NOTE_D5,4, NOTE_C5,8, NOTE_C5,8,
  NOTE_D5,4, NOTE_G5,4, NOTE_E5,4,
  NOTE_F5,2, NOTE_C5,4,
  NOTE_F5,4, NOTE_F5,4, NOTE_F5,4,
  NOTE_E5,2, NOTE_E5,4,
  NOTE_F5,4, NOTE_E5,4, NOTE_D5,4,

  NOTE_C5,2, NOTE_A5,4,//36
  NOTE_AS5,4, NOTE_A5,4, NOTE_G5,4,
  NOTE_C6,4, NOTE_C5,4, NOTE_C5,8, NOTE_C5,8,
  NOTE_D5,4, NOTE_G5,4, NOTE_E5,4,
  NOTE_F5,2, NOTE_C5,4,
  NOTE_F5,4, NOTE_F5,8, NOTE_G5,8, NOTE_F5,8, NOTE_E5,8,
  NOTE_D5,4, NOTE_D5,4, NOTE_D5,4,
  NOTE_G5,4, NOTE_G5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F5,8,
  NOTE_E5,4, NOTE_C5,4, NOTE_C5,4,

  NOTE_A5,4, NOTE_A5,8, NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8,//45
  NOTE_F5,4, NOTE_D5,4, NOTE_C5,8, NOTE_C5,8,
  NOTE_D5,4, NOTE_G5,4, NOTE_E5,4,
  NOTE_F5,2, NOTE_C5,4,
  NOTE_F5,4, NOTE_F5,8, NOTE_G5,8, NOTE_F5,8, NOTE_E5,8,
  NOTE_D5,4, NOTE_D5,4, NOTE_D5,4,
  NOTE_G5,4, NOTE_G5,8, NOTE_A5,8, NOTE_G5,8, NOTE_F5,8,
  NOTE_E5,4, NOTE_C5,4, NOTE_C5,4,

  NOTE_A5,4, NOTE_A5,8, NOTE_AS5,8, NOTE_A5,8, NOTE_G5,8, //53
  NOTE_F5,4, NOTE_D5,4, NOTE_C5,8, NOTE_C5,8,
  NOTE_D5,4, NOTE_G5,4, NOTE_E5,4,
  NOTE_F5,2, REST,4
};

// sizeof gives the number of bytes, each int value is composed of two bytes (16 bits)
// there are two values per note (pitch and duration), so for each note there are four bytes
int notes = sizeof(melody) / sizeof(melody[0]) / 2;

// this calculates the duration of a whole note in ms
int wholenote = (60000 * 4) / tempo;

int divider = 0, noteDuration = 0;

void setup() {
  // iterate over the notes of the melody.
  // Remember, the array is twice the number of notes (notes + durations)
  for (int thisNote = 0; thisNote < notes * 2; thisNote = thisNote + 2) {

    // calculates the duration of each note
    divider = melody[thisNote + 1];
    if (divider > 0) {
      // regular note, just proceed
      noteDuration = (wholenote) / divider;
    } else if (divider < 0) {
      // dotted notes are represented with negative durations!!
      noteDuration = (wholenote) / abs(divider);
      noteDuration *= 1.5; // increases the duration in half for dotted notes
    }

    // we only play the note for 90% of the duration, leaving 10% as a pause
    tone(buzzer, melody[thisNote], noteDuration * 0.9);

    // Wait for the specief duration before playing the next note.
    delay(noteDuration);

    // stop the waveform generation before the next note.
    noTone(buzzer);
  }
}

void loop() {
  // no need to repeat the melody.
}

Yes, possible. Please look into non-blocking coding styles, like the one demonstrated in the BlinkWithoutDelay example, that comes with the Arduino IDE. It uses millis() as a time source, checking regularly, if it is time to do something, otherwise doing something else. There are tons of tutorials on the web about millis(). Please try them first

I think the method suggested by chrisl is probably the best for you to use here.  If you want to make a more-conventional multitasking system, you would set a timer such as TIMER2 to the minimum time slice, then at the start of your code (after setup) you'd have something like this:
ISR(TIMER2_COMPA_vect)
{
myIntHandler();
}
Here myIntHandler() does whatever needs to get done on each tick of TIMER2.  I use this general approach in my open-source DaqPort code to do simultaneous oscilloscope data acquisition and signal generation, while still handling serial commands in the background.
But that's probably overkill for your project. You can find out more about DaqPort at
https://www.daqarta.com/dw_rraa.htm

There are a couple of issues with this approach. The first is that it handles tasks in a “run-to-completion” mode, i.e. the tasks have to be defined as non-blocking functions that return after a single step, and keep static state in order to know what to do next time they are called. This makes the code not much simpler than using the typical millis() / Blink Without Delay technique (or any of the libraries built around this approach). Most people who ask for “multitasking” would expect to be able to delay() a task without blocking the others.

The second issue is that it makes the tasks run in interrupt context. There are a few traps you have to be careful about with code running in interrupt context, like increased latency to other interrupts, or data races. You most likely can handle them, but they can be a problem for beginners.


text:
Arduino Wifi Rev2, bug when reading PWM duty cycle value on pin 6
I am trying to read the PWM duty cycle set for pin 6 on the Arduino Wifi Rev2.
By default, the timer used is TCB0, used in 8-bit PWM mode. The 16-bit Compare channel CCMP is used as two 8-bit registers, the lowest byte CCMPL is for the period, while the highest byte CCMPH is for the duty cycle.
However, something weird happens when I try to read the Compare channel register, the value of CCMPH is unexpecteadly copied into CCMPL.

// PWM on pin 6, timer TCB0
//TCB_t* timer_B0 = (TCB_t *)&TCB0; // pointer on timer structure

void setup() {
  pinMode(6, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  setPWMValueOnPin6(20);
  setPWMValueOnPin6(40);
  setPWMValueOnPin6(60);
  setPWMValueOnPin6(80);
}

void printCompareChannelValues()
{
  Serial.print("Compare channel = ");
  unsigned int r = TCB0_CCMP;
  //byte r = timer_B0->CCMP;
  Serial.println(r);

  Serial.print("Compare channel L = ");
  byte l = TCB0_CCMPL;
  //byte l = timer_B0->CCMPL;
  Serial.println(l);

  Serial.print("Compare channel H = ");
  byte h = TCB0_CCMPH;
  //byte h = timer_B0->CCMPH;
  Serial.println(h);
}

void setPWMValueOnPin6(byte value)
{
  analogWrite(6, value);
  printCompareChannelValues();
  delay(2000);
}

Here is the serial monitor output:
Compare channel = 5375
Compare channel L = 255
Compare channel H = 20
Compare channel = 10260
Compare channel L = 20
Compare channel H = 40
Compare channel = 15400
Compare channel L = 40
Compare channel H = 60
Compare channel = 20540
Compare channel L = 60
Compare channel H = 80
Compare channel = 5200
Compare channel L = 80
Compare channel H = 20
Compare channel = 10260
Compare channel L = 20
Compare channel H = 40
Compare channel = 15400
Compare channel L = 40
Compare channel H = 60
Compare channel = 20540
Compare channel L = 60
Compare channel H = 80
Compare channel = 5200
Compare channel L = 80
Compare channel H = 20

Problem occurs even when removing the serial functions (by watching at a led intensity on pin 6).
The same behavior occurs on pin 3 (timer TCB1).
Does someone has any idea what is going on? Thanks for your help.

Arguable bug location
The problem appears to be here in the analogWrite() code:
    timer_B->CCMPH = val;

I suppose it could be argued that you're using the timer in an way unintended
by the Arduino megaavr core, but I think it's reasonable to consider this as
a bug that should be fixed in the core.
16-bit hardware register access and analogWrite() internals
According to the "Accessing 16-Bit Registers" of the ATMega4809 datasheet (among others no doubt):

The AVR data bus has a width of eight bits, and so accessing 16-bit registers requires atomic operations. These
registers must be byte accessed using two read or write operations. 16-bit registers are connected to the 8-bit bus
and a temporary register using a 16-bit bus.
For a write operation, the low byte of the 16-bit register must be written before the high byte. The low byte is then
written into the temporary register. When the high byte of the 16-bit register is written, the temporary register is
copied into the low byte of the 16-bit register in the same clock cycle.
For a read operation, the low byte of the 16-bit register must be read before the high byte. When the low byte register
is read by the CPU, the high byte of the 16-bit register is copied into the temporary register in the same clock cycle as
the low byte is read. The high byte will now be read from the temporary register.
This ensures that the low and high bytes of 16-bit registers are always accessed simultaneously when reading or
writing the register.
Interrupts can corrupt the timed sequence if an interrupt is triggered and accesses the same 16-bit register during an
atomic 16-bit read/write operation. To prevent this, interrupts can be disabled when writing or reading 16-bit registers.
The temporary registers can be read and written directly from user software.

So in effect, timer_B->CCMPH = val; is not just writing the real CCMPH hardware register, it is also writing the real CCMPL register with whatever is in this anonymous temporary register that was filled by either the last read or write operation.
So here's what's happening in your code:

You execute byte l = TCB0_CCMPL; in printCompareChannelValues() which both reads TCB0_CCMPL and loads the temporary register with the real CCMPH register's value, which as you'll see is important.

You execute byte h = TCB0_CCMPH; which is reading the temporary register rather than the real CCMPH hardware register from which the temporary register was loaded in your read from TCB0_CCMPL above.  Note: technically this statement doesn't play a role in problem.  I'm mentioning it only to drive home the behaviour of the temporary register.

You exit printCompareChannelValues() and exit-and-reenter setPWMValueOnPin6() which calls analogWrite() which inside runs timer_B->CCMPH = val;,
which stores val in the real CCMPH with and stores the temporary register (containing the previous CCMPH value) into the real CCMPL.

So yeah, on each time through the previous CCMPH becomes the new CCMPL value.
Patching the core
Arguably what the core should contain, instead of  timer_B->CCMPH = val;, is something like:
{
    const uint8_t saved_SREG = SREG;
    cli();

    uint16_t temp  = timer_B->CCMP;
    temp          &= 0xFF;
    temp          |= (uint16_t)val << 8;
    timer_B->CCMP  = temp;

    SREG = saved_SREG;
}

Here we're doing full 16-bit register reads.  The load from CCMP reads the CCMPL first which primes the temporary register with the real CCMPH. Then the second half of the load from CCMP tries to read CCMPH, but is really  reading the temporary register correctly containing CCMPH's value.  And now there are no unpleasant surprises when this plays out in reverse.
The statement that assigns CCMP writes the CCMPL (really the temp register) then writes to the real CCMPH and simultaneously commits the temp register to the real CCMPL.
You can patch the above snipped over the timer_B->CCMPH = val; in your PATH-TO-ARDUINO15-DIRECTORY/packages/arduino/hardware/megaavr/CORE-VERSION/cores/arduino/wiring_analog.c
I have not spent a pile of time thinking about that patch, so use a bit of caution.  I may well have forgotten something important.
Priming the temporary register instead
If you don't want to mess with the core or you can't for some reason or another, a ugly hack would be to turn your simple analogWrite(6, value); call into something like the following:
{
  const uint8_t saved_SREG = SREG; cli();
  TCB0_CCMPL = TCB0_CCMPL;
  analogWrite(6, value);
  SREG = saved_SREG;
}

Besides turning off interrupt and then restoring them, the only thing happening here is this somewhat ridiculous looking statement:
TCB0_CCMPL = TCB0_CCMPL;
All this is doing is priming the temporary register for you.  On the right-hand side of the assignment we read the real CCMPL register.  This is also loading the temporary register with the real CCMPH, but we don't care about that.  Then on the left-hand side of the assignment, we attempt to write into CCMPL, but what we're in fact doing is writing to the temporary register.  And now your temporary register has the correct CCMPL value for when you run the timer_B->CCMPH = val; in the original analogWrite() code.
So, I suppose if you want to do this and you're unable/unwilling to modify your core, you could do this, potentially wrapping with an #if #else based on the Arduino megaavr core version, should they update the core in the future with patch given above or something equivalent.
Core patch using the same priming technique
The same priming technique could be applied inside the patch as follows.  This is maybe more confusing but slightly more efficient than the comparatively normal looking first patch.
{
    const uint8_t saved_SREG = SREG; cli();
    timer_B->CCMPL = timer_B->CCMPL; // Prime temporary register
    timer_B->CCMPH = val;
    SREG = saved_SREG;
}

Many thanks @timemage for this very detailed and great answer ! I really like the option of priming the temporary register. I would like to submit this bug to the Arduino team on github so that they take care about correcting the core, and give them the link to this page for patch reference. Are you ok with this?

Sure, you can try it.   Pretty sure I couldn't stop you even if I wanted. =)  For what it's worth it affects the "Nano Every" board as well.  Basically both of the only two boards currently in the arduino:megaavr package.

I submitted this as a bug to be fixed on Arduino github (github.com/arduino/ArduinoCore-megaavr/issues/91). I was asked to do a Pull Request. @timemage Would you be interested to do this PR for your proper attribution?

That is something that will take me a long time to research, decide, and do if that's what I decide.  In short, I don't know.  If you want to talk about it though you can find me on freenode under this name.

I am not on freenode, sorry @timemage. As I would really need this to be fixed, I think the core patch using the priming technique could be a first proposal for the PR, then discussion about what to do or to change could be done on github with the Arduino team.

Okay. Good luck with it.


text:
esp8266 set up?
A few weeks ago I was told to incorporate wifi with an arduino project so I ordered a few arduino ESP8266 boards but when I couldn't figure it out I switched to a simpler board AKA(Arduino Nano IOT 33).  Now that I am done with my time constrained project I would like to learn about the esp8266 can anyone please explain the different settings in the tools
See image down below for example.:


text:
Any way to use DMA to transfer data to SD Card in ESP8266
I'm using ESP8266 at speed of 160 MHz to record ADC results into SD card, I need to do this at rate of 8 KHz. Means that i should save it on the SD Card or i'll run out of memory before 4 Sec passes.
I used timer1 to trigger sampling, but my problem is that surprisingly ESP8266 can not do this fast enough and the maximum sampling rate i get is about 7 KHz. I also tried to write data in ESP8266 Flash using littleFS in hope of acceleration but there was no luck. Here is my code:
#include <SPI.h>
#include <SD.h>

File dataFile;

int flag = 0;
int count = 0, level = 0, se_count = 0;

void inline analogReader() // Sampling and Recording processed which is done sequentially :(
{
  uint16_t dataByte = (uint16_t) analogRead(A0);
  dataFile.write((uint8_t*) &adcBuf, 2);

}

void ICACHE_RAM_ATTR onTimerISR() { // Timer sets the flag every 0.125 MS
  flag = 1;
}

void setup() {
  pinMode(A0, INPUT);
  pinMode(D0, OUTPUT);

  Serial.begin(115200);

  if (!SD.begin(D0)) {
    Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial.println("card initialized.");

  File root = SD.open("/");

  if (SD.exists("real_test.bin"))
  {
    SD.remove("real_test.bin");
  }

  dataFile = SD.open("real_test.bin", FILE_WRITE);

  if (dataFile) {
    Serial.println("File created!");
  }
  else
  {
    Serial.println("Failed to create file!");
    return;
  }

  // Setting the timer so that timer interrupt fires every 0.125 MS
  timer1_attachInterrupt(onTimerISR);
  timer1_enable(TIM_DIV1/*from 80 Mhz*/, TIM_EDGE/*TIM_LEVEL*/, TIM_LOOP/*TIM_SINGLE*/);
  timer1_write(10000); /* ( 80 / 1 ) * 125 */
}

void loop() {
  if (flag) // To check whether its time to sampling or not, because of the low speed of the process
  {         // The flag is set again before one iteration ends.
    flag = 0;

    if (se_count < 10)
      analogReader();

    count++;
    if (count == 8000) // When 8000 sample & write process done notify using serial monitor. (it is
                          always more that 1 Sec. for example 1.3 Sec.)
    {
      count = 0;
      level = !level;
      Serial.println(level);

      se_count++;
      if (se_count == 10) // When we done 8000 * 10 sample & write we close the file. it supposed to
                          // be done in almost 10 sec. but it takes more time in practice. for
                          // example 13 Sec.
      {
        dataFile.close();
        Serial.println("Write ended");
      }
    }

  }
}

I even used a faster method system_adc_read_fast(buf, num, clk_div) that is available in ESP-SDK, So i'm desperately seeking for one or two of approaches bellow if possible:

To use a non-blocking approach: If there is any possibility to hand SD writing to DMA so that i only fill the ADC buffer for DMA, or an interrupt for ADC complete so that i write to SD and when interrupt fired put the result in the buffer and return back to SD writing.
Yet faster approach for both ADC sampling and writing: Can i increase the SD write speed of SD library? for example by configuring SPI for maximum speed rate.

I also tried SDFat library but it does not recognize my SD Card. The whole process is odd to me that even by using a 160 MHz CPU and hardware i can not record ADC data at rate of 8000 Sample/S. I have TIME limitation so please help me.
Any suggestion is appreciated. Thanks in advance.

Please help me guys, There is no suggestion? How possible!

It's not clear to me where your performance bottleneck is happening.  In your code you have an interrupt set a flag but your loop() function takes its sweet time doing things, especially things like writing serial that will likely take longer than the interval to the next interrupt.  I suspect the problem is the overhead of an SD card write operation.  Perhaps you might batch them up so that you write more data to the SD less often.

Thanks. I put the code in loop() instead of interrupt routine itself because it takes longer time than 125 MS or 1/8000 Second to be done. So before finishing interrupt routine the interrupt line reasserted. So CPU never returned to loop() and that watch dog timeout happened. even though i tried to feed the dog in the interrupt routine :) . Ok i agree with you. May be batching can help improving the performance. I'm trying it. I'll report the result.

One problem with your loop() architecture is that if you don't complete the loop before the next interrupt happens, you will miss a reading.  You might redesign this so that the interrupt reads the ADC and stores the result in a buffer.  Then you loop can watch that buffer until it get full, grab that data and start writing to the SD card.  In that case you have N interrupts to complete the task.  N is the number of readings your buffer can hold.

Putting the code in loop() is good. Your line int flag = 0; should be volatile int flag = 0;. This lets the compiler know that flag may change unexpectedly, so it will know to reload it from memory more often. Otherwise flag might change and the loop code might miss it because the value is cached in a register.

Hey guys have you tried to read ADC and put the result into buffer in the timer interrupt service routine? I can not do this and ESP8266 keeps resetting at the timer rate of 8000 Hz which i configured in the code i'v posted, I actually faced this problem before but i thought that it is because i am doing both sampling and writing to SD and it takes long time so watch dog keeps resetting MCU. but now i just tried to sample ADC and put result into RAM. I think this is not taking that long time.

So, sorry for late response, i figured it out. Because it took lots of time for me and it might be a prevalent issue, i think it worth to contribute my experience for further similar issues.
As mentioned buffering can do the job, here are some constraints that should be satisfied to have right buffering mechanism;

Writing to SD card SHOULD NOT be handled in the Timer ISR, but store ADC data into a buffer, aka int[] which is normally kept in SRAM (Your Timer ISR should be as short and fast as needed)

Handle reading from buffer to SD outside of Timer ISR in, for example in loop().

priority of ISR should be more than loop(), and it is so by default.

It's a good practice to divide your buffer into chunk of 512 or 1024. for example 2 chunks of 1024 Bytes making your buffer. you check for "filled" flag to be set in the loop() and when this happens call SDFile.write(..) for corresponding filled chunk and then reset the flag.

By conforming to these constraints you enjoy these benefits:

you can be sure that no ADC on time sampling missed because of sluggish SD write process.

by buffering and pilling data and then storing, you have chance to avoid byte wise write to SD, which is  really inefficient. and instead handle write in chunks of 512 or 1024 bytes. which is equal to sector size of SDs. so you have faster SD write process too.

This way you can easily handle voice recording up to 16 ksps even without using DMA.
I have added the code to my github, in this repository; https://github.com/A-R-S-D/WAVRecorder


text:
controlling 2 sensor and 3 pump using millis
i make a 3 pump for output and im using millis for it, i want to stop if rach the intervalm now in my code its still looping not realy stop for it thx
int pump1 = 5;//pin D5
int pump2 = 6;//pin D6
int pump3 = 7;//pin D7
float pompa1;
float pompa2;
float pompa3;
unsigned long prev_pump1    = 0;
unsigned long prev_pump2    = 0;
unsigned long prev_pump3    = 0;
unsigned long initiate_start = 0;
int pump_state1 = false;
int pump_state2 = false;
int pump_state3 = false;

and after this this is my loop program
  //-----------PUMP ALGORITHM--------------------
  long int pompa01 = pompa1*1000;//interval pump1
  long int pompa02 = pompa2*1000;//interval pump2
  long int pompa03 = pompa3*1000;//interval pump2

  unsigned long initiate_start = millis();

  //pompa1
  if(intiate_start-prev_pump1 > pompa01)
  {
    pump_state1 = !pump_state1;

    if(pump_state1)
    {
      digitalWrite(pump1, HIGH);
    }
    else
    {
      digitalWrite(pump1, LOW);
    }
    prev_pump1 = initiate_start;
  }

    //pompa2
  if(intiate_start-prev_pump2 > pompa02)
  {
    pump_state2 = !pump_state2;

    if(pump_state2)
    {
      digitalWrite(pump2, HIGH);
    }
    else
    {
      digitalWrite(pump2, LOW);
    }
    prev_pump2 = initiate_start;
  }

    //pompa3
  if(intiate_start-prev_pump3 > pompa03)
  {
    pump_state3 = !pump_state3;

    if(pump_state3)
    {
      digitalWrite(pump3, HIGH);
    }
    else
    {
      digitalWrite(pump3, LOW);
    }
    prev_pump3 = initiate_start;
  }

Please, copy-edit your question: make complete sentences, and pay attention to grammar and punctuation. As it stands, it is really hard to decipher.

Side note: The variable names initiate_start and intiate_start are too similar, which makes the code confusing.

This is meant as a complement to Michel Keijzers' answer. I fully agree
with his assessment: code duplication is something to be avoided, as it
greatly hurts maintainability. And I sympathise with him feeling the
urge to implement this as a proper class. ;-)
I would argue, however, that between the simplistic version of the OP's
code, and the fully object-oriented one of that answer, there is an
intermediate step that is worth targeting as the next thing to learn. I
mean: arrays and loops.
Many cases of code duplication follow a pattern roughly like this:
// At the top of the sketch:
some_type data0 = some_value;
some_type data1 = some_other_value;
// etc.

// Within a function:
do_lots_of_stuff(data0);
do_lots_of_stuff(data1);
// etc.

where do_lots_of_stuff() may be a lot of code, rather than a single
function call (and it would indeed not be that bad if it was just a
function call).
The simple solution to this kind of code duplication is to put the
variables in an array, and process them by looping through the array:
// At the top of the sketch:
const int data_count = ...;
some_type[data_count] = {some_value, some_other_value, ...};

// Within a function:
for (int i = 0; i < data_count; i++)
    do_lots_of_stuff(data[i]);

Here is my attempt at applying this technique to the OP's code. Note
that, since there are multiple variables for each pump, we need multiple
arrays:
const int pump_count = 3;
const uint8_t pump_pins[pump_count] = {5, 6, 7};
const uint32_t pump_half_periods[pump_count] = {1000, 2500, 3200};

uint8_t pump_states[pump_count];  // LOW or HIGH
uint32_t pump_last_actuations[pump_count];  // time in ms

void setup() {
    for (int i = 0; i < pump_count; i++) {
        pinMode(pump_pins[i], OUTPUT);
    }
}

void loop() {
    unsigned long now = millis();
    for (int i = 0; i < pump_count; i++) {
        if (now - pump_last_actuations[i] >= pump_half_periods[i]) {
            pump_states[i] = (pump_states[i] == HIGH) ? LOW : HIGH;
            digitalWrite(pump_pins[i], pump_states[i]);
            pump_last_actuations[i] = now;
        }
    }
}

This removes all the duplication, and should be easier to maintain and
debug.
Once one is comfortable with this concept, I would argue the next step
in terms of building abstractions would be to bundle together all the
data relative to one pump into a struct. Then we would have a single
array (the elements of which are structs) instead of a collection of
arrays:
// Data structure describing a pump.
struct Pump {
    const uint8_t pin;
    const uint32_t half_period;  // half period in ms
    uint8_t state;               // LOW or HIGH
    uint32_t last_actuation;     // time in ms
};

const int pump_count = 3;

Pump pumps[pump_count] = {
// pin, half-T, state, last_actuation
    {5, 1000, LOW, 0},
    {6, 2500, LOW, 0},
    {7, 3200, LOW, 0}
};

The body of setup() and loop() would be very similar to the previous
version, but with pump_pins[i] replaced by pumps[i].pin and so on.
The third step in the abstraction ladder would be to embed the code that
handles the data structure with the data itself in a class, as shown
in Michel Keijzers' answer. With the proper methods defined, the main
sketch could be reduced to something like this:
#include "Pump.h"

Pump pumps[] = {
    Pump(5, 1000),
    Pump(6, 2500),
    Pump(7, 3200)
};

void setup() {
    for (Pump &pump : pumps)
        pump.begin();
}

void loop() {
    for (Pump &pump : pumps)
        pump.process();
}

I tried all these methods, and it turns out the first one (with a
collection of arrays) is the one that uses the least lines of code, and
the last one (with a class) uses the most. One could wonder then why use
more advanced abstractions, if they increase the amount of code one has
to type. The reason is, these abstractions become useful when the
program grows in complexity. On can put together all the code that deals
with the pumps in one place (maybe in a separate file), all the code
that deals with sensors in another place... and the main sketch that
puts all the pieces together remains simple.

what is a "half period in ms" ? is it a state time or what

@daffafaiz: The “period” is the time it takes the signal to repeat itself. The “half period” is half the period. “ms” is the symbol for “milliseconds”, a unit of time.

thx for that sir

and sir how if i erase the const it is able to edit isnt ?

@daffafaiz: Excuse me? Can you make a full sentence?

is the const in front of the uint32_t it's for fix data cant change anymore ?

@daffafaiz: Yes, it means we are not going to change that value. If your plan is to change it, then remove the const qualifier.

Note: this is not an answer (my previous version of the answer contained a problem, thanks Edgar Bonet for notifying and a fix for the remainder of the answer). However, I don't want to remove this answer as it may help in removing a lot of duplication. I took the liberty to convert it into a class.
It compiles, but I can't check the functionality but hopefully it helps you to continue. If there are problems, use write commands to send text to the serial to check the times and state.
Sketch:
#include  "Pump.h"

const int NR_OF_PUMPS = 3;

Pump pumps[NR_OF_PUMPS] =
{
  Pump(5, 1.0),
  Pump(6, 2.5),
  Pump(7, 3.2)
};

void setup()
{
}

void loop()
{
  process();
}

void process()
{
  for (int pumpIndex = 0; pumpIndex < NR_OF_PUMPS; pumpIndex++)
  {
    pumps[pumpIndex].Process(millis());
  }
}

Pump.h:
class Pump
{
public:
  Pump::Pump(int pin, float interval);
  void Process(uint32_t currentTime);

private:
  int _pin;
  float _interval;
  uint32_t _prevTime;
  bool _state;
};

Pump.cpp:
#include "Arduino.h"
#include "Pump.h"

Pump::Pump(int pin, float interval)
:
  _pin(pin),
  _interval(interval * 1000.0),
  _prevTime(0),
  _state(false)
{
}

void Pump::Process(uint32_t currentTime)
{
  if (currentTime - _prevTime > _interval)
  {
    _state = !_state;
    digitalWrite(_pin, _state ? HIGH : LOW);
    _prevTime = currentTime;
  }
}

Re “seems that the current time is always put into the prev variables”: the update of the prev variables is within the if condition that controls the actuation of the pump, which is fine. BTW, you forgot to update _prevTime in the example you provide.

@EdgarBonet Good catch, thanks for the improvements.

The
prev_pump3 = initiate_star make the loop forever ? Or what ?

Not that I know, however you have a problem with the typo in the intiate/initiate variable likely.

I solved the typo this however its still lopping :(

Add some print statements to see what's wrong, that's the easiest way to debug these problems

Re “_prevTime(millis())”: global constructors are called before the initialization of the Arduino core. I would avoid calling core functions at this time, even though millis() may be harmless. It is safe to initialize _prevTime to zero, which is the starting value of millis() anyway.

@EdgarBonet I thought about that indeed. I would think the start time is quickly anyway but it could matter. Initially I initialized _prevTime with a separate method during setup but that seemed a bit overdone, as this doesn't seem a time critical application anyway.

Okaaay let me add some println for this thx for helpin me


text:
System crash after using sprintf
My Arduino crashes after using sprintf, To reduce system ram am trying to move away from strings to char and now this happened. Loop not working properly and it's not going to webrequest, spent 2 days to find a solution nothing worked yet. My project is to send temp and humidly to my server i was using strings and after one day of use system very slow to send data to server then i realized stings are the culprit then i changed to char
char ventionoroff[6];

char Foggeronoroff[6] ;

char Exhuastonoroff[6] = "OFF";

char current[4];

char today[10];

bool running = false;
bool isFoggerRunning = false;
bool isExhaustFanRunning = false;

Time t;

void loop() {

 t = rtc.getTime();

  hum = dht.readHumidity();
  temp = dht.readTemperature();
  delay(1000);

  if (isnan(hum) || isnan(temp)) {
    Serial.println(F("Failed to read from DHT sensor!"));
    return;
  }
  val = digitalRead(powerStatus);

      byte MIN = t.hour;
      byte SEC = t.min;

  char timeStamp[20];
 snprintf(today, "%02d:%02d",  MIN, SEC);

   Serial.println(today);

if (millis() - lastConnectionTime > postingInterval) {
   Serial.println("reaching here2");
    httpRequest();
  }

  if (val)
  snprintf( current , "YES");

  else
 snprintf( current , "NO");

  if (running == false) {
    snprintf( Foggeronoroff , "WAIT");

  } else {
   snprintf( Foggeronoroff , "READY");

  }

    ventilationfan();

void httpRequest() {
Serial.println("shit reached here");
  client.stop();
  char data[10];
  const char phpScript[21]  = "";
  const char server[20]  =  ""; // also change the Host line in httpRequest()

   char strt[6];
   char str[6];
  sprintf(strt, "te=%d", temp);
  strcpy(data, strt);
  strcat (data,"&ti=");
  strcat (data,today);
   strcat (data,"&mot=");
   strcat (data,ventionoroff);
   strcat (data,",");
   strcat (data,Foggeronoroff);
    strcat (data,",");
    strcat (data,current);
     strcat (data,"&mo=");
     strcat (data,Exhuastonoroff);
      sprintf(str, "&hum=%d",hum);
      strcat (data,str);

 Serial.println(data);
   int len = strlen(data);
  if (client.connect(server, 80)) {
    client.print("POST ");
    client.print(phpScript);
    client.println(" HTTP/1.1");
    client.println("Host:");
    client.println("User-Agent: Arduino/1.0");
    client.println("Connection: close");
    client.println("Content-Type: application/x-www-form-urlencoded; charset=UTF-8");
    client.print("Content-Length: ");
    client.println(len);
    client.println();
    client.print(data);
    restartnet = 0;
     data[0] = 0;
     Serial.print("data after sucessfull connection");
     Serial.println(data);
     Serial.print("freeMemory after connection");
     Serial.println(freeMemory());
    lastConnectionTime = millis();

  } else {

     Serial.println(F("Failed"));
     data[0] = 0;

  }
}

Do you mean this line? sprintf(strt, "te=%d", temp); - Are you sure, that temp will only have 2 digits? Which type is temp? You didn't include the declaration of it in your question. And why are you doing all the strcat() calls? Why not just using snprintf with a bigger format string, including all the data together?

it doesn't look like data is large enough

And when using snprintf(), shouldn't you also provide the maximum number of characters to write, as this function is meant for that?

@chrisl ttemp is temperature value, i tried to do that but get errors new to sprintf so no idea how to implement to make string

@Juraj new to sprintf , dont know how to deal with it , what i need to do then?

the array size should reflect the count of characters you concatenate into it. does it make sense?

@Juraj Yeah, actually i still don't know why my system  crash.

@Juraj can you show me an example how to declare global char that change status like , on ,off

you rewrite a memory of other variable if you write out of the allocated space. it looks like you write around 30 characters more then your arrays size data[10]

@Juraj i got that, i changed the size to 30. does the system reuse [30] the memory after use?

count the characters and create a large enough array. I said 30 more so I guess 40. it is a local variable allocated on 'stack'.  https://en.wikipedia.org/wiki/Stack-based_memory_allocation

@Juraj After changing to [30] system works looks fine now, if you dont mind can you show me an example to put big strings in sprintf ? because my purpose is to send data to server with webservice  i used strcat insted of sprintf the problem with the strcat ( is i need to empty every time

sprintf_P(buff, PSTR("%-15s|%d-%02d-%02d %02d:%02d:%02d|% 5d|% 5d|% 3u|"), eventLongLabels[ix], year(t), month(t), day(t), hour(t), minute(t), second(t),  events[ix].value1, events[ix].value2, events[ix].count); https://www.cplusplus.com/reference/cstdio/printf/

There are quite a few points that could be improved in this code. The
main ones have already been shown in the comments:

the data buffer was not large enough to hold the whole string,
which leads to a buffer overflow, memory corruption and, ultimately,
the crash you are experiencing.

The second parameter to snprintf() should be the size of the
provided buffer.

Here I will add a few points that, although not really defects of this
program, could help improved it and save memory.
Here:
char current[4];
// ...
if (val)
    snprintf(current, "YES");
else
    snprintf(current, "NO");

you are not using the current buffer to build a string, but only to
store one of two pre-existing strings (the literals "YES" and "NO")
that are already stored in memory. Rather than making a copy, you could
instead reuse those pre-existing strings, and have current be a
pointer to the relevant one:
const char *current;
// ...
if (val)
    current = "YES";
else
    current = "NO";

This only saves two bytes (size of the buffer minus size of the
pointer), but this pattern happens multiple times in the program, and
the savings add up.
Now, another small optimization. Here:
char strt[6];
sprintf(strt, "te=%d", temp);
strcpy(data, strt);

you do not need the extra strt buffer, as you can write directly into
the data buffer. Better yet, you can write everything into the
buffer in a single call to snprintf():
snprintf(data, sizeof data,
        "te=%d&ti=%02d:%02d&mot=%s,%s,%s&mo=%s&hum=%d",
        temp, t.hour, t.min, ventionoroff, Foggeronoroff, current,
        Exhuastonoroff, hum);

If you don't like this approach, and would prefer writing the data in
separate calls to snprintf(), you can do so without using auxiliary
buffers. You have to remember (with a pointer) where within the buffer
you finished your previous write, and pass this address to the next
snprintf() call. Note that you also have to keep track of the room
that is still available, which is easy to do with a second pointer that
points to the end of the buffer:
char *p = data;                  // current writing position
char *end = data + sizeof data;  // end of the buffer
p += snprintf(p, end - p, "te=%d", temp);
p += snprintf(p, end - p, "&ti=%s", today);
// ...

snprintf(data, sizeof data, "te=%d&ti=%d&mot=%s,%s,%s&mo=%s&hu=%d",
temp, today, ventionoroff, Foggeronoroff, current,
Exhuastonoroff, hum);  'today' actualy rtc time , but i get 825 in that char not time , i serial printed the today and i got the time but sending 825 to server dont know where it come

@user2037091: today was an array of char in the original code. Anyway, I amended the answer in order to print t.hour and t.min instead. This saves an extra auxiliary buffer.


text:
Remote camera with Arduino MKR GSM
I'm interested in making a remote camera that could take and process still photos and send over cellular data using the Arduino MKR GSM. Is this feasible with the specs of the arduino? If it is does anyone have any suggestions of camera modules?

If you don't specify more details the answers to this are going to come down to responder's personal preferences.  E.g. how often are you taking pictures, how much data are you allowed to use, what resolution do you need etc.

Most Arduino boards don't even have enough memory to store a single still image. You might be able to get it to work, but it would be a lot of work. Get some other board, like for example the raspberry pi. Or get a cheap/old smartphone. A smartphone is probably cheaper than a board, and camera module and a GSM module combined.

Esp cam would be another option


text:
Why does Arduino round off some integers but not others when my desktop PC does not do this?
I am writing (in C) some low level digit manipulation routines that convert int to float and vice versa for Arduino. I came across some puzzling behaviour with Arduino rounding some numbers but not others. So I located and extracted the offending code into a simple program which I compiled on desktop Linux PC with GCC and on Arduino - they give different results. Why?
Here are the programs. The goal is to take the float number 1.3140 and the float number 2.3140 and split them into two integers. In the first case I expect '1' and '3140' and in the second case I expect '2' and '3140' (the terminal '0' is important for my application).
The Arduino code:
void setup() {
double dnum,x,y;
uint16_t intpart,fracpart;

Serial.begin(9600);

dnum = 1.3140;

 x=modf(dnum,&y);
 intpart = (uint16_t)y;
 fracpart = (uint16_t)(10000.0*x);

Serial.println(dnum,4);
Serial.println(intpart);
Serial.println(fracpart);

}

and the output I get is:
1.3140
1
3140

Fantastic! Success. But hold on. If I change the hard-coded number to dnum = 2.3140 I get this:
2.3140
2
3139

What? Why did it round the second part down? Am I doing something fundamentally wrong? So I wrote a little C program for my PC (compiled with gcc). Here is the code:
#include<stdint.h>
#include<stdio.h>
#include<math.h>

int main (int argc, char *argv[])
{
 double dnum,x,y;
 uint16_t intpart,fracpart;

 dnum = 2.3140;

  x=modf(dnum,&y);
  intpart = (uint16_t)y;
  fracpart = (uint16_t)(10000.0*x);

 printf("Int_part = %u\nFrac_Part = %u\n",intpart,fracpart);

 return 0;
}

When run I get this result:
Int_part = 2
Frac_Part = 3140

Now that's what I expect - and to be thorough I also did the above C program with dnum = 1.3140; and I again got the 'correct' expected result:
Int_part = 1
Frac_Part = 3140

So why does the Arduino round the fractional part down when I convert it to an integer sometimes and not others?
I tried this on a genuine Arduino Uno, a clone Arduino uno and a clone Arduino nano and all give the same results. In 'theory' it could be some difference in the compiler tolerance to some coding impropriety I have done (gcc is more tolerant) but can anyone tell me what I did wrong and how to get the Arduino to behave (the way I want it to)? Thanks.

When you cast a floating point number to an integer, it always gets
rounded towards zero. If you want round-to-nearest behavior, you should
use the Arduino round() function.
As to the origin of the discrepancy you see, it comes from two facts:

The numbers you use are not exactly representable as floating point
numbers.
The double on an Arduino Uno is actually equivalent to a float
(32 bits only).

When you write a statement such as dnum = 1.3140;, the compiler stores
in dnum the floating point number that is closest to the decimal
number 1.3140.
Compare:
float32(1.3140) = 1.31400001049041748046875
float64(1.3140) = 1.31400000000000005684341886080801486968994140625
float32(2.3140) = 2.3139998912811279296875
float64(2.3140) = 2.31400000000000005684341886080801486968994140625

Out of these 4 examples, only float32(2.3140) is rounded down.

Very clear - thanks. I was unaware of the Arduino 32 bit limit but you educated me. I confirmed your answer also by making 'dnum' a float on my desktop PC program and it gives the same answers as my Arduino so practice and theory agree. I don't think round() will help me here, I just need to accept I can't get the precision I want on the Arduino (without additional 'arbitrary precision' code that will overflow my PROGMEM in my application).

@WildCat: If you only need 1+4 decimal digits or so, like in the example you give, a float should suffice, provided you get the factional digits with round(1e4*x). A float is worth about 7 decimal digits (FLT_EPSILON ≈ 1.19e-7).