Repository: johnnyb/Eventually
Branch: master
Commit: d7344fa5d16f
Files: 9
Total size: 22.7 KB
Directory structure:
gitextract_rzhxmiah/
├── LICENSE
├── README.md
├── examples/
│ ├── Blinky/
│ │ └── Blinky.ino
│ ├── ListenerWithData/
│ │ └── ListenerWithData.ino
│ ├── SimpleButtonBlink/
│ │ └── SimpleButtonBlink.ino
│ └── TennisGame/
│ └── TennisGame.ino
├── library.properties
└── src/
├── Eventually.cpp
└── Eventually.h
================================================
FILE CONTENTS
================================================
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 Jonathan Bartlett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
Eventually
==========
An Arduino Event-based Programming Library
------------------------------------------
Arduino programming is a bit of a bait-and-switch. At first, people are like, "look, it's super-easy - see my blink program!" Then, when it comes to writing anything with even the most minute complexity - with states and input and output - one has to switch to a horrid event-programming mindset with thousands of state variables everywhere.
Then, when you have to debounce the buttons, it gets even worse!
The goal of Eventually is to make a more event-oriented environment for Arduino programming. To make it *actually* easy to use to build worthwhile projects.
### Writing a Simple Eventually Program
To show the program in action, we will write a program that blinks a light,when a button is pushed.
To use Eventually, you need to download the library and then include it in your code (Sketch -> Include Library). That should put the following line at the top of your program:
#include <Eventually.h>
Next, you need to include a global variable for the event manager. I usually call this "mgr":
EvtManager mgr;
Eventually programs do not use the loop() function like other Arduino programs. Instead, remove the auto-generated loop() function and in its place put in this line of code (it SHOULD NOT be within a function) to use our event manager instead of the normal Arduino loop (notice that there is NO SEMICOLON at the end):
USE_EVENTUALLY_LOOP(mgr)
Now, during our setup() function, instead of just doing pinModes, you also need to setup one or more listeners. The two primary types of listeners for Eventually are EvtPinListener and EvtTimeListener. We are going to use both. We will use the pin listener to tell us when the button is pushed and the time listener to blink the light. Therefore, our setup function will look like this:
#define LIGHT_PIN 5
#define BUTTON_PIN 3
void setup() {
/* Set the pin modes */
pinMode(LIGHT_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT);
/* Add a button listener */
mgr.addListener(new EvtPinListener(BUTTON_PIN, (EvtAction)start_blinking));
}
Alternatively, if our BUTTON_PIN uses INPUT_PULLUP:
void setup() {
// ...
/* Button is LOW when pushed, so use INPUT_PULLUP */
pinMode(BUTTON_PIN, INPUT_PULLUP);
/* Add a button listener, listening for LOW */
/* (To use this constructor, we also have to specify the debounce delay) */
mgr.addListener(new EvtPinListener(BUTTON_PIN, 40, LOW, (EvtAction)start_blinking));
}
This creates a listener for the given button pin, and will call start_blinking when the button is pushed. Note that by default, EvtPinListeners will automatically debounce the pin, and also make sure that it starts in the opposite state before beginning listening (though both of these are configurable).
Also note that we put (EvtAction) in front of our function name. This makes sure the compiler knows that this will be used as an event action. It may not compile without this marking.
Now, when the button is pushed, we need start_blinking to install a new time-based listener to blink the light, and another button listener to stop the blinking. So the code will look like this:
bool start_blinking() {
mgr.resetContext();
mgr.addListener(new EvtTimeListener(500, true, (EvtAction)blink_pin));
mgr.addListener(new EvtPinListener(BUTTON_PIN, (EvtAction)stop_blinking));
return true;
}
This starts by calling resetContext(), which will uninstall all current listeners to prepare for a new set. Then, it adds a time listener. The first parameter to the EvtTimeListener constructor is the number of milliseconds to wait until firing. In this case, 500 (half a second). The second parameter is whether or not to continually fire. If it is set to false, it will fire once and then be done. The last parameter is the function to call.
Then it adds a pin listener to call the stop_blinking function when pushed. All triggered actions need to return true or false. "true" stops the current action chain and is usually what you want.
Now, let's look at the blink_pin function:
bool blink_state = LOW;
bool blink_pin() {
blink_state = !blink_state;
digitalWrite(LIGHT_PIN, blink_state);
return false;
}
This creates a global variable that tells us the blink state, and whenever this is called, it alternates the blink state and writes that to the pin. This one returns false because, since the blinking doesn't affect the action of other pins, the event chain should continue.
Finally, we need a stop_blinking() function. All this will do is create a new listener to wait for it to start again:
bool stop_blinking() {
mgr.resetContext();
mgr.addListener(new EvtPinListener(BUTTON_PIN, (EvtAction)start_blinking));
return true;
}
Now you are ready to go!
### C++ Issues
One of the goals of this library was to make it use the flexibility of C++ without the user having to know a lot of C++. If you don't know C++, just use the listeners in the basic way mentioned. However, if you want to do more advanced things, this section covers the C++ issues.
One main goal is to take advantage of the flexibility available with C++ pointers but without exposing the user to them. Therefore, listeners are always created with the "new" keyword, but they are immediately added to the manager (technically, the context) which then manages it. Once the listener is added to the manager/context, then the responsibility for its memory management goes to the manager/context. Note that at this time, there is no ability for user code to detect when a listener gets destroyed, so if you add data to a listener (see section below), then it should not rely on the listener to destroy the data at the right time.
### Writing Your Own Listeners
If you need a more advanced listener, you can write your own fairly easily.
You need to create a subclass of EvtListener and add (a) a constructor, (b) a setupListener() method, and (c) an isEventTriggered() method.
The constructor simply takes whatever arguments control the listener's behavior. The setupListener() method gets called when the listener is added to the chain. This should handle doing things such as sampling the current time or pin states that you will need to determine if the event is triggered.
The isEventTriggered() method returns true if the event is triggered.
Below is a simple class that simply always fires:
class EvtAlwaysFiresListener : public EvtListener {
public:
EvtAlwaysFiresListener();
void setupListener();
bool isEventTriggered();
}
EvtAlwaysFiresListener::EvtAlwaysFiresListener() {
// Nothing to construct
}
void EvtAlwaysFiresListener::setupListener() {
// Nothing to setup
}
bool EvtAlwaysFiresListener::isEventTriggered() {
return true;
}
### Adding Data to Listeners
This library also has the ability to add small bits of data to listeners. Each listener had a "data" member, which allows arbitrary data stored into it (type is a pointer to void, and the value defaults to 0). The data variable is completely unmanaged, so use it for whatever you want. It doesn't have to store a pointer, in fact, it is probably easier to manage if it doesn't.
So, for instance, we can rewrite the blink_pin function to not rely on a global variable, like this:
bool blink_pin(EvtListener *lstn) {
lstn->data = !lstn->data;
digitalWrite(LIGHT_PIN, lstn->data);
return false;
}
This uses the data variable to store the pin state. Note that our action function changed signature as well. The actual signature for the action function is (EvtListener *, EvtContext *). However, the C++ stack will allow you to have the function with fewer arguments, as long as they are in the right order. That is why we have to put (EvtAction) in front of our action functions - to get it to the right type. Note that if we want access to more parts of our listener, we can also use the subclass type instead of just EvtListener if we know which one it will be.
### Future Expansion
The two biggest features I want to add are (a) support for multiple contexts, and (b) declarative creation of a state machine. There is some code in there for (a), but it is not ready to use.
================================================
FILE: examples/Blinky/Blinky.ino
================================================
#include <Eventually.h>
/*
* This is the standard blinky lights written using Eventually.
* Just set LIGHT_PIN to whatever pin you have your LED attached to.
*/
#define LIGHT_PIN 5
EvtManager mgr;
bool state = LOW;
void setup() {
pinMode(LIGHT_PIN, OUTPUT);
mgr.addListener(new EvtTimeListener(1000, true, (EvtAction)blinkme));
}
bool blinkme() {
state = !state; // Switch light states
digitalWrite(LIGHT_PIN, state); // Display the state
return false; // Allow the event chain to continue
}
USE_EVENTUALLY_LOOP(mgr) // Use this instead of your loop() function.
================================================
FILE: examples/ListenerWithData/ListenerWithData.ino
================================================
#include <Eventually.h>
/*
* This is the same as the "simple" example, except
* that it shows how to store data in the listener itself.
*/
#define LIGHT_PIN 5
#define BUTTON_PIN 3
bool speed = LOW;
EvtManager mgr;
bool blink(EvtListener *l) {
Serial.write("hello");
int data = (int)l->extraData;
Serial.write(data);
if(data == 0) {
data = 1;
} else {
data = 0;
}
Serial.write(data);
Serial.write("done");
digitalWrite(LIGHT_PIN, data);
l->extraData = (void *)data;
return false;
}
bool set_speed() {
mgr.resetContext();
mgr.addListener(new EvtPinListener(BUTTON_PIN, (EvtAction)set_speed));
speed = !speed; // Change speeds
if(speed == HIGH) {
mgr.addListener(new EvtTimeListener(250, true, (EvtAction)blink));
} else {
mgr.addListener(new EvtTimeListener(1000, true, (EvtAction)blink));
}
return true;
}
void setup() {
pinMode(LIGHT_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT);
set_speed();
}
USE_EVENTUALLY_LOOP(mgr)
================================================
FILE: examples/SimpleButtonBlink/SimpleButtonBlink.ino
================================================
#include <Eventually.h>
/*
* This just shows a simple way of using the library.
* Pushing a button moves the Arduino back-and-forth
* from a fast blink to a slow blink.
*/
#define LIGHT_PIN 5
#define BUTTON_PIN 3
bool speed = LOW;
EvtManager mgr;
bool pin_state = LOW;
bool blink() {
pin_state = !pin_state;
digitalWrite(LIGHT_PIN, pin_state);
return false;
}
bool set_speed() {
mgr.resetContext();
mgr.addListener(new EvtPinListener(BUTTON_PIN, (EvtAction)set_speed));
speed = !speed; // Change speeds
if(speed == HIGH) {
mgr.addListener(new EvtTimeListener(250, true, (EvtAction)blink));
} else {
mgr.addListener(new EvtTimeListener(1000, true, (EvtAction)blink));
}
return true;
}
void setup() {
pinMode(LIGHT_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT);
set_speed();
}
USE_EVENTUALLY_LOOP(mgr)
================================================
FILE: examples/TennisGame/TennisGame.ino
================================================
#include <Eventually.h>
/*
* This is a simple Tennis game using the Eventually library.
* To use, wire up two buttons (one for each player, and then
* a series of lights to be used for the ball. You can
* change PLAYER_BUTTON_1 and PLAYER_BUTTON_2 to whatever you like.
* The ball lights need to be in a sequential series of pins starting
* with BALL_PIN_START and ending with BALL_PIN_END. The code
* will auto-adapt to however many pins you use, as long as you tell
* it where the start and end points are.
*/
#define PLAYER_BUTTON_1 3
#define PLAYER_BUTTON_2 2
#define BALL_PIN_START 5
#define BALL_PIN_END 9
#define BALL_SPEED 1000
#define TURN_DELAY 1000
EvtManager mgr;
int currentPlayer;
int numBallPositions = BALL_PIN_END - BALL_PIN_START + 1;
int lastBallPosition = BALL_PIN_END - BALL_PIN_START;
int currentBallPosition;
void setup() {
pinMode(PLAYER_BUTTON_1, INPUT);
pinMode(PLAYER_BUTTON_2, INPUT);
for(int i = BALL_PIN_START; i <= BALL_PIN_END; i++) {
pinMode(i, OUTPUT);
}
currentPlayer = 1;
readyPlayer();
}
void readyPlayer() {
mgr.resetContext();
if(currentPlayer == 1) {
currentBallPosition = 0;
} else {
currentBallPosition = lastBallPosition;
}
showBall(currentBallPosition);
mgr.addListener(new EvtPinListener(PLAYER_BUTTON_1, (EvtAction)startNextPlayer));
}
void showBall(int offset) {
for(int i = 0; i + BALL_PIN_START <= BALL_PIN_END; i++) {
if(i == offset) {
digitalWrite(BALL_PIN_START + i, HIGH);
} else {
digitalWrite(BALL_PIN_START + i, LOW);
}
}
}
void showAll() {
for(int i = 0; i + BALL_PIN_START <= BALL_PIN_END; i++) {
digitalWrite(BALL_PIN_START + i, HIGH);
}
}
bool startNextPlayer() {
mgr.resetContext();
mgr.addListener(new EvtTimeListener(BALL_SPEED, true, (EvtAction)moveBall));
if(currentPlayer == 1) {
currentPlayer = 2;
mgr.addListener(new EvtPinListener(PLAYER_BUTTON_2, (EvtAction)swingRacket));
currentBallPosition = 0;
} else {
currentPlayer = 1;
mgr.addListener(new EvtPinListener(PLAYER_BUTTON_1, (EvtAction)swingRacket));
currentBallPosition = lastBallPosition;
}
return true;
}
bool moveBall() {
if(currentPlayer == 1) {
currentBallPosition--;
if(currentBallPosition < 0) {
return missedBall();
}
} else {
currentBallPosition++;
if(currentBallPosition > lastBallPosition) {
return missedBall();
}
}
showBall(currentBallPosition);
return false;
}
bool swingRacket() {
if((currentPlayer == 1 && currentBallPosition == 0) || (currentPlayer == 2 && currentBallPosition == lastBallPosition)) {
return hitBall();
} else {
return missedBall();
}
}
bool hitBall() {
// If we hit, it moves to be the next player's ball
return startNextPlayer();
}
bool missedBall() {
mgr.resetContext();
showAll();
currentPlayer = 1;
mgr.addListener(new EvtTimeListener(TURN_DELAY, false, (EvtAction)readyPlayer));
return true;
}
USE_EVENTUALLY_LOOP(mgr)
================================================
FILE: library.properties
================================================
name=Eventually
version=0.1.5
author=Jonathan Bartlett <jonathan@bartlettpublishing.com>
maintainer=Jonathan Bartlett <jonathan@bartlettpublishing.com>
sentence=Event-based programming library for Arduino
paragraph=This library is meant to make Arduino programming tasks much more simplified by using an event-driven model rather than the standard looping model.
category=Other
url=http://www.github.com/johnnyb/Eventually
architectures=*
includes=Eventually.h
================================================
FILE: src/Eventually.cpp
================================================
/*
* This program is copyright 2016 by Jonathan Bartlett. See LICENSING
* file for information on usage (MIT License).
* Be sure to check out my books at www.bplearning.net!
*/
#include <Eventually.h>
/* *** EVT MANAGER *** */
EvtManager::EvtManager() {
contextStack = new EvtContext[EVENTUALLY_MAX_CONTEXTS];
contextStack[contextOffset].setupContext();
}
void EvtManager::addListener(EvtListener *lstn) {
contextStack[contextOffset].addListener(lstn);
}
void EvtManager::removeListener(EvtListener *lstn) {
contextStack[contextOffset].removeListener(lstn);
}
EvtContext *EvtManager::currentContext () {
return &contextStack[contextOffset];
}
EvtContext *EvtManager::pushContext() {
contextOffset++;
contextStack[contextOffset].setupContext();
return &contextStack[contextOffset];
}
EvtContext *EvtManager::resetContext() {
contextStack[contextOffset].setupContext();
return &contextStack[contextOffset];
}
EvtContext *EvtManager::popContext() {
contextOffset--;
return &contextStack[contextOffset];
}
void EvtManager::loopIteration() {
contextStack[contextOffset].loopIteration();
}
/* *** EVT CONTEXT *** */
EvtContext::EvtContext() {
}
void EvtContext::loopIteration() {
for(int i = 0; i < listenerCount; i++) {
if(listeners[i]) { // Make sure it isn't deleted
if(listeners[i]->isEventTriggered()) { // If we are triggered, run the action
if(listeners[i]->performTriggerAction(this)) { // If the action returns true, stop the chain
return;
}
}
}
}
}
void EvtContext::setupContext() {
if(data){
delete data;
}
if(listeners) {
for(int i = 0; i < listenerCount; i++) {
if(listeners[i]) {
delete listeners[i];
}
}
delete listeners;
}
listeners = new EvtListener *[EVENTUALLY_MAX_LISTENERS];
listenerCount = 0;
}
void EvtContext::addListener(EvtListener *lstn) {
int i = 0;
// find the first empty slot
for(; i < listenerCount; i++) {
if(listeners[i] == 0) {
break;
}
}
// if we didn't find one, expand
if(i == listenerCount) {
listenerCount++;
}
listeners[i] = lstn;
lstn->setupListener();
}
void EvtContext::removeListener(EvtListener *lstn) {
for(int i = 0; i < listenerCount; i++) {
if(listeners[i] == lstn) {
delete lstn;
listeners[i] = 0;
}
}
}
/* *** EVT LISTENER *** */
void EvtListener::setupListener() {
}
bool EvtListener::isEventTriggered() {
return false;
}
bool EvtListener::performTriggerAction(EvtContext *ctx) {
return (*triggerAction)(this, ctx);
}
/* *** EVT PIN LISTENER *** */
EvtPinListener::EvtPinListener() {
}
EvtPinListener::EvtPinListener(int pin, int debounce, bool targetValue, EvtAction action) {
this->pin = pin;
this->debounce = debounce;
this->targetValue = targetValue;
this->triggerAction = action;
}
EvtPinListener::EvtPinListener(int pin, int debounce, EvtAction action) {
this->pin = pin;
this->debounce = debounce;
this->triggerAction = action;
}
EvtPinListener::EvtPinListener(int pin, EvtAction action) {
this->pin = pin;
this->triggerAction = action;
}
void EvtPinListener::setupListener() {
startState = digitalRead(pin);
}
bool EvtPinListener::isEventTriggered() {
bool val = digitalRead(pin);
// Debounce check if we were triggered earlier
if(firstNoticed) {
unsigned long curMillis = millis();
if(curMillis > firstNoticed + debounce) {
// Debounce time expired, check again
// Reset Watcher
firstNoticed = 0;
// Check
if(val == targetValue) {
return true;
} else {
return false;
}
} else {
// Waiting for debouncer to finish
return false;
}
}
if(mustStartOpposite && (startState == targetValue)) {
/* This is a waiting loop to wait for the pin to change to the opposite state before sensing */
/* Q - do I need to debounce mustStartOpposite? */
if(val == startState) {
// Do nothing
} else {
startState = val;
}
return false;
} else {
/* This is the real deal */
if(val == targetValue) {
if(debounce == 0) {
return true;
} else {
firstNoticed = millis();
return false;
}
} else {
return false;
}
}
}
/* *** EVT TIME LISTENER *** */
EvtTimeListener::EvtTimeListener() {
}
EvtTimeListener::EvtTimeListener(unsigned long time, bool multiFire, EvtAction t) {
this->millis = time;
this->triggerAction = t;
this->multiFire = multiFire;
}
void EvtTimeListener::setupListener() {
startMillis = ::millis();
}
bool EvtTimeListener::isEventTriggered() {
unsigned long curTime = ::millis();
bool shouldFire = false;
if(curTime >= startMillis) {
/* Normal */
if(curTime - startMillis > this->millis) {
shouldFire = true;
}
} else {
/* Wrap-Around! */
if(((ULONG_MAX - startMillis) + curTime) > this->millis) {
shouldFire = true;
}
}
return shouldFire;
}
bool EvtTimeListener::performTriggerAction(EvtContext *c) {
bool returnval = (*triggerAction)(this, c);
if(multiFire) {
// On multifire, setup to receive the event again
setupListener();
// On multifire, we shouldn't stop the event chain no matter what, since we are just restarting in this context
return false;
} else {
// remove this listener from the chain
// else it will keep triggering each loop
c->removeListener(this);
return returnval;
}
}
================================================
FILE: src/Eventually.h
================================================
/*
* This program is copyright 2016 by Jonathan Bartlett. See LICENSING
* file for information on usage (MIT License).
* Be sure to check out my books at www.bplearning.net!
*/
#ifndef EVENTUALLY_H
#define EVENTUALLY_H
#include <limits.h>
#include <Arduino.h>
#define EVENTUALLY_MAX_CONTEXTS 10
#define EVENTUALLY_MAX_LISTENERS 20
class EvtManager;
class EvtContext;
class EvtListener;
typedef bool (*EvtAction)(EvtListener *, EvtContext *);
class EvtManager {
public:
EvtManager();
void loopIteration();
EvtContext *pushContext();
EvtContext *resetContext();
EvtContext *popContext();
EvtContext *currentContext();
void addListener(EvtListener *lstn);
void removeListener(EvtListener *lstn);
private:
EvtContext *contextStack = 0;
int contextOffset = 0;
int contextDepth = 0;
};
// Note - should probably expand the number of available listeners by chaining contexts
class EvtContext {
public:
void *data = 0;
EvtContext();
void setupContext();
void loopIteration();
void addListener(EvtListener *lstn);
void removeListener(EvtListener *lstn);
private:
EvtListener **listeners = 0;
int listenerCount;
};
class EvtListener {
public:
void *extraData = 0; // Anything you want to store here
EvtAction triggerAction;
virtual void setupListener();
virtual bool isEventTriggered();
virtual bool performTriggerAction(EvtContext *); // return false if I should stop the current chain
protected:
};
class EvtPinListener : public EvtListener {
public:
EvtPinListener();
EvtPinListener(int pin, EvtAction trigger);
EvtPinListener(int pin, int debounce, EvtAction action);
EvtPinListener(int pin, int debounce, bool targetValue, EvtAction action);
int pin = 0;
int debounce = 40;
bool targetValue = HIGH;
bool mustStartOpposite = true;
bool startState;
unsigned long firstNoticed = 0;
void setupListener();
bool isEventTriggered();
};
class EvtTimeListener : public EvtListener {
public:
EvtTimeListener();
EvtTimeListener(unsigned long time, bool multiFire, EvtAction trigger);
unsigned long millis;
void setupListener();
bool isEventTriggered();
bool performTriggerAction(EvtContext *);
private:
unsigned long startMillis;
bool multiFire = false;
int numFires = 0;
};
#define USE_EVENTUALLY_LOOP(mgr) void loop() { mgr.loopIteration(); }
#endif
gitextract_rzhxmiah/
├── LICENSE
├── README.md
├── examples/
│ ├── Blinky/
│ │ └── Blinky.ino
│ ├── ListenerWithData/
│ │ └── ListenerWithData.ino
│ ├── SimpleButtonBlink/
│ │ └── SimpleButtonBlink.ino
│ └── TennisGame/
│ └── TennisGame.ino
├── library.properties
└── src/
├── Eventually.cpp
└── Eventually.h
SYMBOL INDEX (8 symbols across 2 files)
FILE: src/Eventually.cpp
function EvtContext (line 23) | EvtContext *EvtManager::currentContext () {
function EvtContext (line 27) | EvtContext *EvtManager::pushContext() {
function EvtContext (line 33) | EvtContext *EvtManager::resetContext() {
function EvtContext (line 38) | EvtContext *EvtManager::popContext() {
FILE: src/Eventually.h
function class (line 22) | class EvtManager {
function class (line 42) | class EvtContext {
function class (line 70) | class EvtPinListener : public EvtListener {
function class (line 87) | class EvtTimeListener : public EvtListener {
Condensed preview — 9 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (25K chars).
[
{
"path": "LICENSE",
"chars": 1084,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Jonathan Bartlett\n\nPermission is hereby granted, free of charge, to any person"
},
{
"path": "README.md",
"chars": 8437,
"preview": "Eventually\n==========\nAn Arduino Event-based Programming Library\n------------------------------------------\n\nArduino pro"
},
{
"path": "examples/Blinky/Blinky.ino",
"chars": 582,
"preview": "#include <Eventually.h>\n\n/*\n * This is the standard blinky lights written using Eventually.\n * Just set LIGHT_PIN to wha"
},
{
"path": "examples/ListenerWithData/ListenerWithData.ino",
"chars": 988,
"preview": "#include <Eventually.h>\n\n/* \n * This is the same as the \"simple\" example, except\n * that it shows how to store data in t"
},
{
"path": "examples/SimpleButtonBlink/SimpleButtonBlink.ino",
"chars": 842,
"preview": "#include <Eventually.h>\n\n/* \n * This just shows a simple way of using the library.\n * Pushing a button moves the Arduino"
},
{
"path": "examples/TennisGame/TennisGame.ino",
"chars": 3001,
"preview": "#include <Eventually.h>\n\n/*\n * This is a simple Tennis game using the Eventually library.\n * To use, wire up two buttons"
},
{
"path": "library.properties",
"chars": 461,
"preview": "name=Eventually\nversion=0.1.5\nauthor=Jonathan Bartlett <jonathan@bartlettpublishing.com>\nmaintainer=Jonathan Bartlett <j"
},
{
"path": "src/Eventually.cpp",
"chars": 5513,
"preview": "/*\n * This program is copyright 2016 by Jonathan Bartlett. See LICENSING\n * file for information on usage (MIT License)"
},
{
"path": "src/Eventually.h",
"chars": 2374,
"preview": "/*\n * This program is copyright 2016 by Jonathan Bartlett. See LICENSING\n * file for information on usage (MIT License)"
}
]
About this extraction
This page contains the full source code of the johnnyb/Eventually GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 9 files (22.7 KB), approximately 6.0k tokens, and a symbol index with 8 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.