Arduino Events

Will this substantially slow the no-event yield() case?

Right now it's pretty bad with the 6 serial events and 1 usb serial.
 
Code:
    bool transfer(const void *txBuffer, void *rxBuffer, size_t count, EventResponderRef event_responder);

Internally you'd store the EventResponderRef as a pointer, but pointer syntax is generally not accepted for Arduino APIs so a reference is used to pass it in. Then later when the operation completes, call its triggerEvent() function.

I don't see any need to pass another pointer in this case, since there's already 2 for the data. The event supports a data pointer, but most cases probably won't make any use of it.
So with this Syntax, I then assume that passing in en EventResponder is not optional. Which is probably not an issue, although with SPI there were cases where I did not really need one, but instead had a couple of methods. One that asked if the transfer was done and another waited until it was done.

So assuming the above, I would think I could remove these two other methods?

I would also assume that I could simply do something like:
Code:
    EventResponder eresp;
    SPI.transfer(mybuf, nullptr, count, eresp);
    .... 
    // Check to see if it is done
    if (!eresp) {
        // has not triggered yet
    }

    // wait for the SPI to be done.
    eresp.waitForEvent(eresp, my_timeout);

Again I may be off on syntax, and the like, but with the above I am assuming:

a) Since I did not attach a function, this will not call any functions and will simply set the status and data and _pending?

b) The bool operator returns _pending, so can find out if the event was triggered.

c) waitForEvent? I am assuming that this will wait for an event to happen. But wondering if this will be a static method? Else why do I pass in EventResponderRef? Could image two flavors, either it is static, and can ask for one or multiple (your list version) or simply wait for my specific one... Not sure if that made sense?

Another related question, If my TeensyView class needs to hold onto a EventResponder object, should it initialize it as part of begin/start method:
_event_responder.setContext(this); // Set the contxt to us
_event_responder.attachImmediate(&TeensyView::asyncEventResponder);
Or should it do it on each call that will setup an async operation, or should these functions instead call clearEvent() to make sure the _pending has been cleared...
 
One problem I can see with run to completion function calls is that when I use "delay" in the main program that dead time could be much longer than i want it for if any of the events take time with things like busy loops. This could be issue with newcomers in that they will ask why is my "delay" function longer than I programed it to be and it would not be that predictable. If you context switch with yield then you spread out the processing more and things like delays are not as effected as bad (you can yield back). But I guess you will have to lay down a strict guidelines for making events run as fast a possible like any function called from interrupt.
 
Will this substantially slow the no-event yield() case?

Right now it's pretty bad with the 6 serial events and 1 usb serial.

With a warm cache, your original EventResponder::runFromYield() costs 26 CPU cycles on Teensy 3.6, my version 37 cycles (for empty event lists).
 
I've added a MillisTimer object, which triggers EventResponder events on either a one-shot or repeating basis.

https://github.com/PaulStoffregen/cores/commit/a587b3dd1e849d1262f49bf17c91c52ecb04c2a2

Here's a really simple test program.

Code:
#include <EventResponder.h>

EventResponder myevent;
EventResponder mytimerevent;
EventResponder mytimerevent2;
MillisTimer mytimer;
MillisTimer mytimer2;

void setup() {
  while (!Serial) ;
  Serial.println("EventResponder test");
  myevent.attach(dosomething);
  mytimerevent.attach(dotimer);
  mytimerevent2.attach(dotimer2);
  mytimer2.beginRepeat(750, mytimerevent2);
}

void loop() {
  Serial.println("loop trigger dosomething");
  myevent.triggerEvent();
  delay(4000);
}

void dosomething(EventResponderRef event)
{
  Serial.println("dosomething event");
  mytimer.begin(550, mytimerevent);
}

void dotimer(EventResponderRef event)
{
  Serial.println("oneshot timer event");
}

void dotimer2(EventResponderRef event)
{
  Serial.println("  repeating timer event");
}
 
Hi Paul,

I am still playing along here. I keep syncing up to your changes.

Still working with the SPI updates to use this. With your proposed way of passing the EventResponder by reference, as I mentioned in previous posting, was wondering about suggested way of doing something where you may not necessarily need something called, but more like you simply want to know if your operation completed and maybe wait...

Example: suppose, I am doing a version of the ILI9341 library that I don't wish to know anything about fifo queues... But instead use Async (DMA) support of SPI, and assume you won't let me get away with adding SPI.transfer16() with buffers and Async. Then what I might do when I am doing a fillRect or ... That outputs a region of the screen, is to create two buffers, fill the first one with data (transpose the color bytes), start up SPI async operation and start filling the 2nd buffer. When the 2nd buffer is full, I wish to wait until the first transfer completes and then start 2nd...

Before the EventResponder I had methods on SPI to ask of an async transfer was still active and likewise a method to wait... Wondering suggest way here?

Obviously I can do something like:
Code:
uint8_t active_spi_transfer = 0;
EventResponder  eresp;

void my_func(EventResponderRef erf) {
    active_spi_transfer = 0;
}

...
    eresp.attachImmediate(my_func);
...
    active_spi_transfer = 1;
    SPI.transfer(Buffer1, nullptr, cnt, eresp);
    // Do stuff to fill up buffer 2
    while (active_spi_transfer) ;
   ...

But wondering if most of the above could be done internally with the Event Responder object... Like for example don't call attach... Maybe use wait for event? Maybe something like:
Code:
EventResponder  eresp;

...
    eresp.clearEvent();
    SPI.transfer(Buffer1, nullptr, cnt, eresp);
    // Do stuff to fill up buffer 2
    eresp.waitEvent(?);
   ...
Currently in header file you have:
Code:
	// Wait for event(s) to occur.  These are most likely to be useful when
	// used with a scheduler or RTOS.
	bool waitForEvent(EventResponderRef event, int timeout);
	EventResponder * waitForEvent(EventResponder *list, int listsize, int timeout);
With no implementations. With the first one, wondering if you need to pass an event to it? As not static method, so maybe just timeout?

Second one, I would think this would be a static member, where you wait on multiple objects and in this case maybe more used in RTOS? Although I could see using it without RTOS, where I am waiting for something to do, where I get inputs from several places...
 
Still working with the SPI updates to use this. With your proposed way of passing the EventResponder by reference, as I mentioned in previous posting, was wondering about suggested way of doing something where you may not necessarily need something called, but more like you simply want to know if your operation completed and maybe wait...
IMO, there should be an event-free async API. Since the EventResponder parameter apparently can't be a pointer, it needs to be a separate method.

Before the EventResponder I had methods on SPI to ask of an async transfer was still active and likewise a method to wait...
IMO, it makes sense to keep those in addition to EventResponder.

But wondering if most of the above could be done internally with the Event Responder object...
Since the user needs to allocate those dummy EventResponder objects and needs to be concerned about the lifetime, that's rather ugly and kludgy.
 
@tni - I agree with your points, but I can also see someone say KISS and find a decent way using the Event objects...

For the code I am hacking up, I simply am holding onto two of these objects are part of the class. One where I wish to do something special (like launch another DMA transfer) and the second where it simply says it is done... Not eloquent.

Also assuming I can use the event object to test the state? I assume that is the operator bool() which returns pending. And that I can use waitForEvent, my logical code would look more like:
Code:
EventResponder  eresp;

...
    while (more data) {
        // fill first buffer
        erest.waitForEvent(...) ;
        eresp.clearEvent();
        SPI.transfer(Buffer1, nullptr, cnt, eresp);
      // Do stuff to fill up buffer 2
      eresp.waitEvent(?);
      erest.clearEvent();
      SPI.transfer(Buffer2, nullptr, cnt, eresp);
   }
And you also run into the issue that the first waitForEvent will likely hang forever as nothing will trigger the first event...
So would probably need to internally call eresp.triggerEvent() outside of the while loop, in order to prime the loop correctly...
which may not be pretty.
 
Still WIP - but pushed up a branch of my SPI fork that has the event responders... So far only with T3.x... Next up TlC

https://github.com/KurtE/SPI/tree/Spi-multi-EventResponder

I am testing now with both my version of Teensyview as well as a hacked up version of ili9341_t3n library that does not use Pushr/popr, but instead tries to speed things up using buffer writes and Async writes.

Still debugging some issues and also wondering about some semantics. Example:
SPI.transfer(buffer, nullptr, count, eresp) where count = 0
Should SPI trigger the event even though it did not do anything? Probably yes.

Also fixing issue with count = 1, as my dma handling is giving error response as I start the transfer with first byte/word being done by PUSHR direct to cleanup and as such count goes to zero for DMA request which errors...

Also wondering in my above code, when you call something and pass in response object, should I as the caller be the one that clears the event or should the function I call with the event object clear it. That is should the SPI.transfer(..., eresp) call should transfer start off with: eresp.clearEvent() ?
 
Quick update: I have been playing with the SPI and a version of ili9341 library (ili9341_t3ns) which is based off of my _t3n library except it knows nothing about FIFOS and tries to make use of Async SPI calls, now with the EventResponder.

I have been running into some interesting issues with my code hanging. It worked fine when I had my debug printing turned on, but would hang on my first Async SPI call.

I localized it down to which Serial outputs were needed to make it work. I then commented it out, I then replaced with a sleep(1);
And it worked. I then replaced the sleep(1) with sleepMicroseconds(100), did not work, tried 250 did not work, tried 1000 did not work.
So then I wondered and replaced with a call to yield(); It worked!

I thought this code was working before when I grabbed version(s) of eventResponder.

Currently I think my core that I am compiling with is up to date as of an hour or so ago.

In case any one is interested in playing along, here is my version of SPI, the ili9341_t3ns library and test app.

Edit: Forgot to mention, that the code is setup to work on my board, where using non standard SPI pins, plus I have several IO pins setup to toggle or set high/low (0, 1, 2, 13)
 

Attachments

  • SPI with Events.zip
    832.1 KB · Views: 787
Last edited:
What skills do you expect users to have? How do they go from setup(), loop() to events?

How are you evaluating the API? It might be good to have realistic example problems/applications to test the API.

I was recently in contact with scientists at CERN to see what technology they were using in control/data acquisition systems. I worked on LHC at CERN for a while just before I retired.

LHC is the world's largest particle accelerator with a circumference of 27 kilometers and has 10,000 superconducting magnets. The control system talks to 85,000 devices.

I was shocked to learn they are not using a RTOS in LHC.

CERN uses "soft CPUs" implemented in a FPGA instead of traditional micro-controllers to access hardware. They implement as many CPUs as needed for each hardware front-end board. The programs look like lots of Arduino loops.

The advantage is simplicity, scientists can develops embedded code with less training.

Here is the philosophy:

Take an FPGA chip.

Implement a CPU core that is deterministic.

Put as many CPUs as needed on a board.

Let them communicate with each other...

...and with the external world.

Up to 8 LM32 cores, with private code/data memory.

Programed in bare metal C, using standard GCC tool chain.

Each core runs a single task in a tight loop.

No caches, no interrupts.

It would be nice if you maintained the Arduino flavor since it's easy to understand. Maybe implemented with event flags and loops. Low priority loops run in a single context and real-time loops have their own context.

The standard API for event flags is setEvent(bits), clearEvent(bits), waitEventOr(bits), and waitEventAnd(bits).
Code:
ISR(vector) {
 setEvent(myEvent);
}

// could run with it's own context or shared context.
void loop5() {

 waitEventOr(myEvents);

 // process event.

}

You can implement this with very little overhead/memory. I did an example a few years ago and the kernel used about one KB of flash. I implemented I2C that allowed the caller to sleep.

Here is an example with Arduino loop() and two real-time loops. It has semaphores instead of event flags.

Uses 1930 bytes on a Uno.
Code:
/*
 * Example to demonstrate thread definition, semaphores, and thread sleep.
 */
#include <NilRTOS.h>

// The LED is attached to pin 13 on Arduino.
const uint8_t LED_PIN = 13;

// Declare a semaphore with an inital counter value of zero.
SEMAPHORE_DECL(sem, 0);
//------------------------------------------------------------------------------
/*
 * Thread 1, turn the LED off when signalled by thread 2.
 */
// Declare a stack with 128 bytes beyond context switch and interrupt needs.
NIL_WORKING_AREA(waThread1, 128);

// Declare the thread function for thread 1.
NIL_THREAD(Thread1, arg) {
  while (TRUE) {
    
    // Wait for signal from thread 2.
    nilSemWait(&sem);
    
    // Turn LED off.
    digitalWrite(LED_PIN, LOW);
  }
}
//------------------------------------------------------------------------------
/*
 * Thread 2, turn the LED on and signal thread 1 to turn the LED off.
 */
// Declare a stack with 128 bytes beyond context switch and interrupt needs. 
NIL_WORKING_AREA(waThread2, 128);

// Declare the thread function for thread 2.
NIL_THREAD(Thread2, arg) {

  pinMode(LED_PIN, OUTPUT);
  
  while (TRUE) {
    // Turn LED on.
    digitalWrite(LED_PIN, HIGH);
    
    // Sleep for 200 milliseconds.
    nilThdSleepMilliseconds(200);
    
    // Signal thread 1 to turn LED off.
    nilSemSignal(&sem);
    
    // Sleep for 200 milliseconds.   
    nilThdSleepMilliseconds(200);
  }
}
//------------------------------------------------------------------------------
/*
 * Threads static table, one entry per thread.  A thread's priority is
 * determined by its position in the table with highest priority first.
 * 
 * These threads start with a null argument.  A thread's name may also
 * be null to save RAM since the name is currently not used.
 */
NIL_THREADS_TABLE_BEGIN()
NIL_THREADS_TABLE_ENTRY("thread1", Thread1, NULL, waThread1, sizeof(waThread1))
NIL_THREADS_TABLE_ENTRY("thread2", Thread2, NULL, waThread2, sizeof(waThread2))
NIL_THREADS_TABLE_END()
//------------------------------------------------------------------------------
void setup() {
  // Start Nil RTOS.
  nilSysBegin();
}
//------------------------------------------------------------------------------
void loop() {
  // Not used.
}
 
Interesting stuff Bill Greiman :: https://github.com/greiman/NilRTOS-Arduino.

Funny 'those scientists' have atypical non-trivial hardware customized to do what is needed - so the software seems easy - with the complexity in silicon. Though with 27 Km to cover localized processing is quite distributed.

The idea of multi-cpu chips is catching on, there are even Arduino format boards with a three cpu chip.

This is the prototype for the three cpu board:
Code:
void setup() {
  // put your setup code for CPU0 here, to run once:
}
void loop() {
  // put your main code for CPU0 here, to run repeatedly:
}
void setup1() {
  // put your setup code for CPU1 here, to run once:
}
void loop1() {
  // put your main code for CPU1 here, to run repeatedly:
}
void setup2() {
  // put your setup code for CPU2 here, to run once:
}
void loop2() {
  // put your main code for CPU2 here, to run repeatedly:
}

LHC is a collaboration with over 10,000 scientists and engineers from over 100 countries. Much of the software development is done by scientists with limited embedded systems experience. The trend in these large projects has been for engineers to build tools to make software development easier. The bulk of the control and application software is done by scientists.

Engineering groups at large labs resisted the idea of developing software tools for decades. Engineers wanted to play with the big hardware for the experiment.

Huge international collaborations changed that. The World Wide Web was another great development by CERN. The web was conceived at CERN in 1989 for information-sharing between scientists in universities and institutes around the world.

My point is that many of the people reading this topic on events have skills to make development of real time applications on Arduino easier. You really need some users that understand their technical science/engineering problem but are not expert embedded systems developers to test you ideas.

Paul is right, first impressions about new real-time features for Arduino are really important. I learned that you can't just drop a full blown RTOS on users, not even on experienced application programmers.
 
First of all, I don't have answers to all these questions. EventResponder is still very much a work in progress.

The main goal has been to keep the API simple, like you'd expect from Arduino. You trigger events, you attach a function to run when it happens, or poll/wait for the event if you don't want a function to run automatically when the event gets triggered.

My hope is this simple API can be implemented on cooperative schedulers and preemptive RTOSs and even true multi-processor systems. Ideally all that complexity can be hidden within the implementation, so libraries and programs using the API can run on more advanced systems with improved performance, but the same code can run on even 8 bit AVR.

There's pretty good anecdotal evidence that Arduino users can understand and use this "function runs when something happens" model. The oldest example is attachInterrupt. People understand it, at least the "function runs when pin changes" model, though properly using interrupt context is a huge pitfall.

The MIDI library is another long established example. Early in Arduino's history there were 2 widely used MIDI libs. At first, Francois Best's library only supported a polling API for incoming messages. An alternate MIDI library was popular in the early days (before 2012) which called C++ functions. The author didn't understand function pointers, but made a C++ class with virtual functions and the user was expected to create a C++ subclass. The alternate MIDI lib became popular, because many beginners greatly preferred the model of getting their function called automatically, rather than having to check return code and write a bunch of if-else checks. When I added USB MIDI on Teensy 2.0, I went with Francois's API (the alternate lib had all different names) and added on a Teensy-only API for function callbacks, but I modeled the callbacks mostly based on the alternate library. Later Francois adopted these into his library. We exchanged a few emails at the time. He'd heard from many users expressed a strong preference for function called when a particular type of message arrives. Since then, that once-popular MIDI lib with the C++ inheritance seems to have faded away (at least beyond the first page of a couple quick google searches).

The Arduino devs added serialEvent and their version of the USB Host library has similar event function calls for Keyboard and Mouse input. While serialEvent isn't implemented for performance, there are many beginners who seem to like using it. I'd take that as another anecdote. However, these use pre-defined weak function names, rather than creating a C++ instance for the response and an explicit myevent.attach(). Whether those 2 extra lines of code are a burden to novices is a good question?

As I mentioned, EventResponder is still very experimental. I have a couple more things I want to do with it before 1.38-beta3 (the first installer to have EventResponder). I've also exchanged a few private emails with the Arduino folks, and I'd like to have them a little more time for input. But the general idea is to get to a sort of first "alpha" (where the API becomes less likely to change) with the 1.38-beta3, and then start trying to actually make use of it and see how well it works in practice.
 
Paul: let me know if there is anything you would like me to do to help.

Like: do you wish to pull in any SPI work that uses this?

Thanks
 
I downloaded the EventResponder and tried the example in post #30 after changing beginRepeat() to beginRepeating().

I will try to implement something I would normally do with RTOS threads.
 
Remember events are more like interrupts than threads. You're supposed to quickly do whatever you're going to do. Interrupts aren't blocked, but other events are (at least when not running this on top of an RTOS).
 
Paul - did you note that this - or another development - may lead to yield() losing current the check/call code to multiple serialEvent#()'s?
 
at least when not running this on top of an RTOS

I don't intend to run EventResponder on top of a RTOS. I don't see anything EventResponder adds to a modern RTOS.

I will implement an RTOS version and then a version with bare metal Arduino and EverntResponder.

I want to see how well EventResponder works for an application that is simple with an RTOS.

I am going to try a data logging example with an ADS1115 16-bit ADC. I will handle the ADS1115 in a thread running at a higher priority than loop.

The ASD1115 thread is a loop that runs at 500 Hz. It wait on sleepUntil(time) and then writes an I2C start conversion message to the ADC. Finally it waits for an ADC done interrupt. Next it does an I2C read of the data and puts the data in a FIFO.

loop() watches the FIFO for data and writes data to a SD.

A key measure of data quality in is jitter in sample time. A good test is to log a pure sine signal, do a FFT and look at the harmonics. Time jitter kills performance of a 16-bit ADC. I don't intend to do an analysis based on a FFT.

For the ADS115 I will log the time of the conversion done interrupt. I looked at the ADC active pin on a Keysight scope that measures pulse widths. The conversion time seems to be stable at 1.1387 -1.1389 ms so I assume the sample and hold time is a fixed interval before the conversion done interrupt. The time will vary with temperature since the ADS1115 has a 1MHz internal oscillator.

I don't have any idea how I can use EventResponder so I will need help/advice.
 
I have the ADS1115 RTOS version working. The code is attached.

I am trying to replace this while loop in a thread that handles the ADC.

Code:
  while (1) {
    // Time for next record.
    logTime += LOG_INTERVAL_TICKS;
    
    // Wait until time for next sample.
    chThdSleepUntil(logTime);

    // Start ADC conversion.
    writeReg(ADS_ADD, CONFIG_REG, EXAMPLE_CONFIG);
    
    // Wait for ADC done interrupt.
    msg_t rtn = chBSemWaitTimeout(&isrSem, LOG_INTERVAL_TICKS);
    
    // Remember ADC done time.
    uint32_t m = micros();
    
    if (rtn != MSG_OK) {
      Serial.println("Missing ADC ready interrupt");
      while (true) {}
    }
    // Get a free record.
    if (chSemWaitTimeout(&fifoSpace, TIME_IMMEDIATE) != MSG_OK) {
      // FIFO full indicate missed point
      error++;
      continue;
    }
    // Put data in FIFO record.
    FifoItem_t* p = &fifoArray[fifoHead];    
    p->usec = m;
    p->value = readData(ADS_ADD);
    p->error = error;
    error = 0;

    // Signal loop() there is new data.
    chSemSignal(&fifoData);
    
    // Advance FIFO head.
    fifoHead = fifoHead < (FIFO_SIZE - 1) ? fifoHead + 1 : 0;
  }

The first step is easy, I just use beginRepeating() to replace the functionality of chThdSleepUntil(logTime). Fortunately this example is fairly slow, the interval is two milliseconds so beginRepeating() works. When the repeating event happens, I use Wire to write a start conversion message to the ADC.

Next I need an event triggered by an ADC done interrupt. That event must use Wire to read data from the ADC. How do I trigger an event from the attachInterrupt() fucntion that runs within a few microseconds with interrupts enabled? On many boards Wire uses Interrupts and reading the data will take over 100 microseconds.
 

Attachments

  • chAds1115Logger.zip
    3 KB · Views: 306
Here is an example with no RTOS stuff.

Currently when an ADC done interrupt occurs, a counter is incremented in the adcDoneInterrupt() function and I read the ADC in loop() and print it.

I want to put an EventResponder call in the adcDoneInterrupt() function that will cause adcRead() to be executed. I will then remove the adcRead() call from loop.

I want adcRead() to happen with interrupts enabled. On Teensy 3.6 it works with the adcRead() call in adcDoneInterrupt(), it takes about 138 microseconds. It fails on a Uno since Wire will not execute using attachInterrupt.

In the real application adcRead() will put data in a FIFO and loop will read data from the FIFO and write it to an SD.

How do I do this with the EventResponder?

Code:
// Example to test Event Responder.
#include "Wire.h"
const uint8_t ADC_ALERT_PIN = 2;
const uint8_t CONFIG_REG = 1;
const uint8_t DATA_REG   = 0;
const uint8_t LO_THRESH  = 2;
const uint8_t HI_THRESH  = 3;
const uint8_t I2C_ADD = 0x48;
const uint16_t ADC_CONFIG = 0xC5E8;
const uint16_t HI_THRESH_READY = 0x8000;
const uint16_t LO_THRESH_READY = 0x0000;

volatile uint16_t adcData = 0;
volatile uint32_t count = 0;
//------------------------------------------------------------------------------ 
void adcDoneInterrupt() {
  // Need eventResponder call that will cause adcRead
  // to be executed with interrupts enabled.
  
 // Move count++ to adcRead when EventResponder is used.  
  count++;  
}
//------------------------------------------------------------------------------
void adcRead() {
  Wire.beginTransmission(I2C_ADD);
  Wire.write(DATA_REG);
  Wire.endTransmission();
  Wire.requestFrom(I2C_ADD, (uint8_t)2);
  
  // Will store data in a FIFO in real app.
  adcData = (Wire.read() << 8) | Wire.read();
}
//------------------------------------------------------------------------------
void adcWriteReg(uint8_t add, uint8_t reg, uint16_t val) {
  Wire.beginTransmission(add);
  Wire.write(reg);
  Wire.write(val >> 8);
  Wire.write(val & 0xFF);
  Wire.endTransmission();
}
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);
  while(!Serial);
  Serial.println("Start");

  // Attach Alert pin to interrupt.
  pinMode(ADC_ALERT_PIN, INPUT_PULLUP); 
  attachInterrupt(digitalPinToInterrupt(ADC_ALERT_PIN), adcDoneInterrupt, RISING);
  
  // Setup for ADC done interrupts.
  Wire.begin();  
  Wire.setClock(400000);
  adcWriteReg(I2C_ADD, HI_THRESH, HI_THRESH_READY); 
  adcWriteReg(I2C_ADD, LO_THRESH, LO_THRESH_READY);
}
//------------------------------------------------------------------------------
void loop() {
  uint32_t tmp = count;
  
  // Start ADC conversion.
  adcWriteReg(I2C_ADD, CONFIG_REG, ADC_CONFIG);
  
  // Wait for ADC done
  while (count == tmp) {}
  
  adcRead();  // Remove when EventResponder works
  
  Serial.print(count);
  Serial.print(',');
  Serial.println(adcData);
  while (count > 9);
  delay(500);
}

This is output from the program. The ADC input is floating.
Start
Start
1,4495
2,4433
3,4391
4,4347
5,4319
6,4433
7,4435
8,4457
9,4553
10,4498
 
Last edited:
Hopefully a simply question?

Suppose I wish to use EventResponder if it exists. How do I test for it?

For example in the SPI library, if I add code that makes use of it, but then the library is used on an older version of Teensyduino, how do I test for it?

That is I assume if my code does: #include <EventResonder.h> and the file does not exist, the compiler will fail, like:
Code:
"D:\arduino-1.8.2\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -felide-constructors -std=gnu++11 -fno-rtti -mthumb -mcpu=cortex-m0plus -fsingle-precision-constant -D__MKL26Z64__ -DTEENSYDUINO=137 -DARDUINO=10802 -DF_CPU=48000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-ID:\arduino-1.8.2\hardware\teensy\avr\cores\teensy3" "-IC:\Users\kurte\Documents\Arduino\libraries\SPI" "C:\Users\kurte\AppData\Local\Temp\arduino_build_85486\sketch\Teensyview_ScreenDemo.ino.cpp" -o "C:\Users\kurte\AppData\Local\Temp\arduino_build_85486\preproc\ctags_target_for_gcc_minus_e.cpp"
In file included from C:\Users\kurte\Documents\Arduino\Teensy Tests\Teensyview_ScreenDemo\Teensyview_ScreenDemo.ino:21:0:

C:\Users\kurte\Documents\Arduino\libraries\SPI/SPI.h:25:28: fatal error: EventResponder.h: No such file or directory

compilation terminated.

So what is the proper way? Maybe Something defined in Arduino.h or one of the files included by it? Or have EventResponder.h included in Arduino.h... ???

Edit: I know that I could potentially test for version number? TEENSYDUINO >= 138

Thanks
 
Last edited:
@KurtE:

GCC >= 5 can check for include files:

Code:
#if defined(__has_include) && __has_include(<EventResponder.h>)
#warning "Have EventResponder.h."
#else
#warning "Don't have EventResponder.h."
#endif

The pre-processor performs short-circuit evaluation, so the above check works for old TD versions with GCC < 5.
 
Back
Top