// JDN 2021
// rt-esp32-fifobuffer01


int bufPutOverWrite(int v);  // for compiler so I can be called just below
int bufPutDrop(int v);

// ISR
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

// Repetetive timer which takes care of sampling etc.
// 1-10kHz

void IRAM_ATTR onTimer() {
  static int sampleVal = 0;

  // FAKE PART
  sampleVal += 5; // in real life read ADC converter aka Arduino way  sampleVal = analogRead(3);
  sampleVal %= 1000;  // a rampe :-)

  // REAL PART
  portENTER_CRITICAL_ISR(&timerMux); // for dataprotection on dual core

  bufPutDrop(sampleVal);

  portEXIT_CRITICAL_ISR(&timerMux);

}

/* setup timer on ISR just above
    variable timerMicroSecCount set timer speed
    Can be used for sampling
*/

const int timerMicroSecCount = 1000;  // 1e6 = 1 sec

void startCyclicTimerISR()
{

  timer = timerBegin(0, 80, true);  // timer no 0 out of 4(0,1,2,3) divide 80 MHz timer with 80 -> 1 MHz timer
  timerAttachInterrupt(timer, &onTimer, true);
  timerAlarmWrite(timer, timerMicroSecCount, true);  // true: auto reload
  timerAlarmEnable(timer);  // gogo
}

// A buffersystem to be put in between an ISR and your code
//
// aka ringbuffe or cyclic buffer

// OUR DATA
const int BUFSZ = 50;  //

// buffer with buffer elements of integer type
uint32_t cyclicBuf[BUFSZ]; // the buffer where we receive data from ISR

volatile int rdIndex = 0, wrIndex = 0, nrInBuf = 0, overFlowCnt = 0;


//-- two variants of writing to buf

// save in buf if space
// if no space just drop saving
// == you loose newest data
// return 0: ok, -1: data drop no saving

void bufInit()
{
  rdIndex = wrIndex = nrInBuf = overFlowCnt = 0;
}

int bufPutDrop(int sampleVal)  // to be called from ISR
{
  if (BUFSZ <= nrInBuf ) { // vacancy ?
    overFlowCnt++;
    return -1;      // nope
  }

  nrInBuf++;        // another on in buf
  wrIndex++;        // where to write element in buffer
  wrIndex %= BUFSZ; // wrap around if end of buf:.. 1%5 = 1  4%5 = 4  5%5 = 0

  cyclicBuf[wrIndex] = sampleVal;
  return 0;         // ok
}


// save in buf if space
// if no space just overWrite oldest elm
// return 0: ok, -1: ok, buf buffer overwrite == loosing oldest data

int bufPutOverWrite(int sampleVal) {  // to be called from ISR
  int retV = 0;
  wrIndex++;   // find location to write
  wrIndex %= BUFSZ;  // wrap around if end of buf:.. 1%5 = 1  4%5 = 4  5%5 = 0

  if (wrIndex == rdIndex)  { // we are full
    rdIndex++;      // push read index in front of write index
    rdIndex %= BUFSZ;
    overFlowCnt++;       // we did overwrite an item
    retV = -1;
  }
  else {
    nrInBuf++;
  }
  cyclicBuf[wrIndex] = sampleVal;
  return retV;
}

// get elm from buf
// return 0: ok, -1: no elements, positive: ok but value is number of elements overwritten since last call
int bufGet(int *v) {
  int of;
  portENTER_CRITICAL(&timerMux);
  if (nrInBuf <= 0) {
    portEXIT_CRITICAL(&timerMux);
    return -1; // no elements in buffer

  }

  *v = cyclicBuf[rdIndex];  // get element

  rdIndex++;      // read index for next time
  rdIndex %= BUFSZ;
  nrInBuf--;

  of = overFlowCnt;
  overFlowCnt = 0;  // reset

  portEXIT_CRITICAL(&timerMux);

  return of;
}


void setup() {
  Serial.begin(115200);
  delay(2000);
  bufInit();
  startCyclicTimerISR();
}

void loop() {
  int  v2, errCode;

  errCode = bufGet(&v2);
  if (0 <= errCode) {
    Serial.println(v2);
  }
  delay(1);
}
