├── .gitignore ├── CHANGELOG.md ├── CPPLINT.cfg ├── CalibrationModels └── regression.py ├── LogParsers ├── ReadMe.txt ├── readBat.py ├── readHMD.py ├── readHPM.py ├── readHPM10.py └── readHPM2.py ├── README.md ├── SensorGridLoRaCollector ├── SensorGridLoRaCollector.ino ├── config.cpp ├── config.h ├── wifi.cpp └── wifi.h ├── SensorGridPM ├── SensorGridPM.ino ├── config.cpp ├── config.h ├── oled.cpp ├── oled.h ├── runtime.cpp └── runtime.h ├── ardcomm.py ├── cad ├── HPMFeatherWing │ ├── HPMFeatherWing.b#1 │ ├── HPMFeatherWing.b#2 │ ├── HPMFeatherWing.b#3 │ ├── HPMFeatherWing.b#4 │ ├── HPMFeatherWing.b#5 │ ├── HPMFeatherWing.b#6 │ ├── HPMFeatherWing.brd │ ├── HPMFeatherWing.pro │ ├── HPMFeatherWing.s#1 │ ├── HPMFeatherWing.s#2 │ └── HPMFeatherWing.sch └── HPMFeatherWing_v2 │ ├── HPMFeatherWing.b#1 │ ├── HPMFeatherWing.b#2 │ ├── HPMFeatherWing.b#3 │ ├── HPMFeatherWing.b#4 │ ├── HPMFeatherWing.b#5 │ ├── HPMFeatherWing.b#6 │ ├── HPMFeatherWing.b#7 │ ├── HPMFeatherWing.b#8 │ ├── HPMFeatherWing.b#9 │ ├── HPMFeatherWing.brd │ └── HPMFeatherWing.sch ├── legacy ├── ADAFRUIT_SI1145.cpp ├── ADAFRUIT_SI1145.h ├── ADAFRUIT_SI7021.cpp ├── ADAFRUIT_SI7021.h ├── ADAFRUIT_ULTIMATE_GPS.cpp ├── ADAFRUIT_ULTIMATE_GPS.h ├── BUILD.md ├── COLLECTOR.md ├── CONFIG.TXT ├── CONFIGURATIONS.md ├── DEPLOYMENT.md ├── Datalogger │ ├── Datalogger.ino │ ├── SHARP_GP2Y1010AU0F.cpp │ └── SHARP_GP2Y1010AU0F.h ├── EEPROM │ └── EEPROM.ino ├── GROVE_AIR_QUALITY_1_3.cpp ├── GROVE_AIR_QUALITY_1_3.h ├── Honeywell │ ├── Honeywell.h │ ├── Honeywell.ino │ └── oled.cpp ├── MODULES.md ├── PLANNING.md ├── README.md ├── RadioHead_1.79.tar.gz ├── RadioTest │ └── RadioTest.ino ├── SHARP_GP2Y1010AU0F.cpp ├── SHARP_GP2Y1010AU0F.h ├── SensorGrid.h ├── SensorGrid.ino ├── WINC1500.cpp ├── WINC1500.h ├── api.py ├── config.cpp ├── config.h ├── display.cpp ├── display.h ├── io.h ├── lib │ └── dtostrf.h ├── requirements.txt ├── timer.cpp ├── timer.h ├── util.cpp └── wsgi.py ├── lib ├── KnightLab_ArduinoUtils │ ├── library.properties │ ├── readme.txt │ └── src │ │ ├── KnightLab_ArduinoUtils.cpp │ │ ├── KnightLab_ArduinoUtils.h │ │ └── arch │ │ ├── WatchdogSAMD.cpp │ │ └── WatchdogSAMD.h ├── KnightLab_FeatherUtils │ ├── library.properties │ └── src │ │ ├── KnightLab_FeatherUtils.cpp │ │ └── KnightLab_FeatherUtils.h ├── KnightLab_Sensors │ ├── library.properties │ └── src │ │ ├── HONEYWELL_HPM.cpp │ │ ├── HONEYWELL_HPM.h │ │ ├── KL_ADAFRUIT_SI7021.cpp │ │ ├── KL_ADAFRUIT_SI7021.h │ │ ├── KnightLab_SensorConfig.cpp │ │ ├── KnightLab_SensorConfig.h │ │ ├── KnightLab_SensorInterface.h │ │ └── base.h ├── TaskScheduler │ ├── LICENSE.txt │ ├── README │ ├── README.md │ ├── examples │ │ ├── Scheduler_example01 │ │ │ └── Scheduler_example01.ino │ │ ├── Scheduler_example02 │ │ │ └── Scheduler_example02.ino │ │ ├── Scheduler_example03 │ │ │ └── Scheduler_example03.ino │ │ ├── Scheduler_example04_StatusRequest │ │ │ └── Scheduler_example04_StatusRequest.ino │ │ ├── Scheduler_example05_StatusRequest │ │ │ └── Scheduler_example05_StatusRequest.ino │ │ ├── Scheduler_example06_IDLE │ │ │ └── Scheduler_example06_IDLE.ino │ │ ├── Scheduler_example07_WDT │ │ │ └── Scheduler_example07_WDT.ino │ │ ├── Scheduler_example08_LTS │ │ │ └── Scheduler_example08_LTS.ino │ │ ├── Scheduler_example09_TimeCritical │ │ │ └── Scheduler_example09_TimeCritical.ino │ │ ├── Scheduler_example10_Benchmark │ │ │ └── Scheduler_example10_Benchmark.ino │ │ ├── Scheduler_example11_Priority │ │ │ └── Scheduler_example11_Priority.ino │ │ ├── Scheduler_example12_Priority │ │ │ └── Scheduler_example12_Priority.ino │ │ ├── Scheduler_example13_Micros │ │ │ └── Scheduler_example13_Micros.ino │ │ ├── Scheduler_example14_Yield │ │ │ └── Scheduler_example14_Yield.ino │ │ ├── Scheduler_example15_STDFunction │ │ │ └── Scheduler_example15_STDFunction.ino │ │ ├── Scheduler_example16_Multitab │ │ │ ├── Scheduler_example16_Multitab.ino │ │ │ ├── file1.cpp │ │ │ ├── file2.cpp │ │ │ ├── header.hpp │ │ │ └── ts.cpp │ │ ├── Scheduler_example17_Timeout │ │ │ └── Scheduler_example17_Timeout.ino │ │ ├── Scheduler_example18_StatusRequest_LTS_WDT_Timeout │ │ │ └── Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino │ │ ├── Scheduler_example19_Dynamic_Tasks │ │ │ └── Scheduler_example19_Dynamic_Tasks.ino │ │ └── Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object │ │ │ ├── Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino │ │ │ ├── SuperSensor.cpp │ │ │ └── SuperSensor.h │ ├── extras │ │ ├── TaskScheduler.doc │ │ ├── TaskScheduler.pdf │ │ └── TaskScheduler_html.png │ ├── keywords.txt │ ├── library.json │ ├── library.properties │ └── src │ │ ├── TaskScheduler.h │ │ └── TaskSchedulerDeclarations.h └── readme.txt ├── platformio.ini.example └── resources ├── README.md └── RadioHead.1.8.2.tar.gz /.gitignore: -------------------------------------------------------------------------------- 1 | platformio.ini 2 | __pycache__ 3 | *.pyc 4 | *.DS_Store 5 | 6 | # PlatformIO 7 | .pioenvs 8 | .piolibdeps 9 | .clang_complete 10 | .gcc-flags.json 11 | 12 | KnightLab_SensorGrid/.vscode/* 13 | KnightLab_SensorGrid/.gitignore 14 | KnightLab_SensorGrid/lib/KnightLab_SDConfig/* 15 | resources/RadioHead.1.8.2/ 16 | # SensorGridPM 17 | SensorGridPM/lib/KnightLab_SDConfig/ 18 | .vscode/* 19 | .vscode/.browse.c_cpp.db* 20 | .vscode/c_cpp_properties.json 21 | .vscode/launch.json 22 | .travis.yml 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v1.1 (2019-09-11) 2 | * establishes baseline functionality for LoRa based wireless collection with new routerless protocol and arduino-LoRa in lieu of RadioHead 3 | * routes are now managed by the collector which controls the full request-response cycle for data 4 | * time is now set via the protocol instead of manually by the user. Time originates from the collector via NTP 5 | * ID for sensor nodes is now extracted from the lsb of the upper part of the Si7012 serial number. Optionally, the node ID can still be set in SD config 6 | * SD config and Adalogger no longer required. (Assumes default network configuration and node ID from sensor serial #) 7 | * Introduces dynamic TX power selection based on RSSI and SNR 8 | 9 | ## v1.0 (2019) 10 | * establishes baseline SD card based data acquisition used for experimental calibration runs. 11 | 12 | ## v0.5 (2018-07-05) 13 | * increases delay between hpm samples 14 | * PR #64 converts namespace-based sensor drivers into classes 15 | * PR #66 deletes old log files (currently > 30 days) 16 | 17 | ## v0.4 (2018-07-03) 18 | * Adds a timeout to STANDBY mode to avoid possible endless standby loops 19 | 20 | ## v0.3 (2018-07-02) 21 | * PR #59. Abstracts iteration of sensors into linked-list of sensor configs 22 | * Adds battery level thresholding power save / recharge mode 23 | 24 | ## v0.2 (2018-06-27) 25 | 26 | * PR #52. Standardizes the sensor API to setup, start, read, stop 27 | * Fixes formatting of floats and unsigned longs in output data 28 | 29 | ## v0.1 (2018-06-27) 30 | 31 | Initial beta release appropriate for calibration runs. Supports the Honeywell 32 | HPM particulate matter sensor (PM2.5 & PM10) and the Adafruit Si7021 temperature 33 | and humidity breakout. Configurable sample cycle (>= 1 minute), data 34 | collection cycle, and "heartbeat" cycle. Primary suppport is for collection to 35 | SD card, but DO_TRANSMIT_DATA config.h flag supports a crude mechanism for 36 | sending data from a single node to a collector. 37 | -------------------------------------------------------------------------------- /CPPLINT.cfg: -------------------------------------------------------------------------------- 1 | filter=-build/include_subdir 2 | extensions=ino,cpp,h 3 | linelength=160 4 | -------------------------------------------------------------------------------- /CalibrationModels/regression.py: -------------------------------------------------------------------------------- 1 | import numpy as numpy 2 | import pandas as pd 3 | import scipy.stats as stats 4 | import matplotlib.pyplot as plt 5 | import sklearn 6 | import sklearn.cross_validation 7 | import seaborn as sns 8 | import pickle 9 | from sklearn.metrics import mean_squared_error 10 | 11 | 12 | from sklearn.linear_model import LinearRegression 13 | 14 | datafile = input("Please input data file: ") 15 | # CalPurple.csv 16 | data = pd.read_csv(datafile) 17 | 18 | X = data.drop('EPA 2.5', axis = 1) 19 | X = X.drop('time', axis = 1) 20 | Y = data['EPA 2.5'] 21 | 22 | # Segments data into testing and training 23 | X_train, X_test, Y_train, Y_test = sklearn.cross_validation.train_test_split(X, Y, test_size = 0.33, random_state = 5) 24 | 25 | # Trains model 26 | lm = LinearRegression() 27 | lm.fit(X_train, Y_train) 28 | 29 | # Saves trained model 30 | # filename = 'test_model.sav' 31 | filename = 'test_model.pckl' 32 | filename = input("Please input model name with .pckl extension: ") 33 | pickle.dump(lm, open(filename, 'wb')) 34 | 35 | 36 | # Formats new test data into right format for regression model 37 | d = {'Purple 2.5': [24], 'tmp': [91.03], 'hmd': [41.93]} 38 | df = pd.DataFrame(data=d) 39 | 40 | # Loads saved model 41 | loaded_model = pickle.load(open(filename, 'rb')) 42 | result = loaded_model.score(X_test, Y_test) 43 | 44 | # Test case for inputting new data into model 45 | test_pred = loaded_model.predict(df) 46 | 47 | x1, y1 = [0, 25], [0, 25] 48 | plt.plot(x1, y1,marker = 'o') 49 | 50 | # Plots predition labels vs. actual labels from saved model 51 | Y_pred = loaded_model.predict(X_test) 52 | plt.scatter(Y_test, Y_pred) 53 | plt.xlabel("EPA PM: $Y_i$") 54 | plt.ylabel("Predicted PM: $\hat{Y}_i$") 55 | plt.title("EPA PM vs Predicted PM: $Y_i$ vs $\hat{Y}_i$") 56 | 57 | print("Mean squared error: " + str(mean_squared_error(Y_pred, Y_test))) 58 | 59 | plt.show() 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /LogParsers/ReadMe.txt: -------------------------------------------------------------------------------- 1 | To use: 2 | 3 | First, put all the log files from one period into a single directory 4 | In the command line, write “cat *.txt > name.txt” to merge all files into one (Where name is whatever you want to call it) 5 | Place the parser you want to use in the directory that holds the new merged log file 6 | 7 | - readBat.py records the battery level 8 | - readHPM.py records both 2.5 and 10 particulate matter 9 | - readHPM2.py records just 2.5 pm 10 | - readHPM10.py records just 10 pm 11 | 12 | The HPM parsers only record values under a specific hpmMAX value that can be changed in the code if wanted 13 | 14 | Once you run the scraper, you will be asked to input the log file to parse, which will be called what you named it earlier. Then you will be asked to input the name of the CSV to write to such as batterylevel.csv 15 | 16 | The data will then be formatted in the csv as: 17 | - Timestamp, Battery, HPM 2.5, HPM 10, Time 18 | What columns show up depends on which parser you use. Time stamp is an epoch time, while time is formatted as MM-DD Hour:Minute 19 | 20 | In excel, you can use the scatter plot to visualize the data. Or use Flourish to best visualize and compare the data 21 | 22 | 23 | -------------------------------------------------------------------------------- /LogParsers/readBat.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pylab as plt 2 | import time 3 | 4 | # BATTERY LEVEL DATA 5 | 6 | 7 | bat_occur = [] 8 | data = {} # The dictionary where we will store results. 9 | substr = "bat" # Substring to use for search. 10 | logfile = input("Please input logfile: ") 11 | csvfile = input("Please input CSV file to write to: ") 12 | 13 | 14 | def find_between(s, first, last): 15 | try: 16 | start = s.index(first) + len(first) 17 | end = s.index(last, start) 18 | return s[start:end] 19 | except ValueError: 20 | return '' 21 | 22 | 23 | with open (logfile, 'rt') as in_file: # Open file for reading text. 24 | csv = open(csvfile, "w") # Open csv file to record lods 25 | csv.write("Timestamp, battery level, time\n") 26 | for linenum, line in enumerate(in_file): # Keep track of line numbers 27 | if line.lower().find(substr) != -1: #If case-insensitive substring search matches, then: 28 | ts = find_between(line, "ts", "}") # Find timestamp data 29 | level = find_between(line, "bat", "ts") # Find battery level 30 | ts = ts.replace(":",'') 31 | ts = ts.replace('"','') 32 | level = level.replace(":",'') 33 | level = level.replace('"','') 34 | level = level.replace(',','') 35 | data[ts] = level 36 | t = time.strftime('%m-%d %H:%M', time.localtime(int(ts))) 37 | row = ts + ',' + level + ',' + t + '\n' 38 | csv.write(row) -------------------------------------------------------------------------------- /LogParsers/readHMD.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pylab as plt 2 | import time 3 | 4 | 5 | # Humidity 6 | 7 | hmd_occur = [] 8 | data = {} # The dictionary where we will store results. 9 | substr = "hmd" # Substring to use for search. 10 | logfile = input("Please input logfile: ") 11 | csvfile = input("Please input CSV file to write to: ") 12 | 13 | def find_between(s, first, last): 14 | try: 15 | start = s.index(first) + len(first) 16 | end = s.index(last, start) 17 | return s[start:end] 18 | except ValueError: 19 | return '' 20 | 21 | 22 | with open (logfile, 'rt') as in_file: # Open file for reading text. 23 | csv = open(csvfile, "w") # Open CSV file to record log 24 | csv.write("Timestamp, hmd, time\n") 25 | for linenum, line in enumerate(in_file): # Fine lines of relevant data 26 | if line.lower().find(substr) != -1: # If case-insensitive substring search matches, then: 27 | hmd_occur.append((linenum, line.rstrip('\n'))) # strip linebreaks, store line and line number in list as tuple. 28 | ts = find_between(line, "ts", "}") # Find timestamp data 29 | hmd = find_between(line, "hmd", ",") # Find humidity data 30 | hmd = hmd.replace('":','') 31 | ts = ts.replace('":','') 32 | ts = ts.replace('}','') 33 | 34 | if ((int(ts) - 7) % 3600 == 0): # Records hourly 35 | t = time.strftime('%m-%d %H:%M', time.localtime(int(ts))) 36 | row = ts + ',' + hmd + ',' + t + '\n' 37 | csv.write(row) -------------------------------------------------------------------------------- /LogParsers/readHPM.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pylab as plt 2 | import time 3 | 4 | 5 | # 2.5 and 10 HPM DATA 6 | 7 | hpm_occur = [] 8 | data = {} # The dictionary where we will store results. 9 | substr = "hpm" # Substring to use for search. 10 | hpmMAX = 40 # Upper limit of feasible HPM readings 11 | logfile = input("Please input logfile: ") 12 | csvfile = input("Please input CSV file to write to: ") 13 | 14 | def find_between(s, first, last): 15 | try: 16 | start = s.index(first) + len(first) 17 | end = s.index(last, start) 18 | return s[start:end] 19 | except ValueError: 20 | return '' 21 | 22 | 23 | with open (logfile, 'rt') as in_file: # Open file for reading text. 24 | csv = open(csvfile, "w") # Open CSV file to record log 25 | csv.write("Timestamp, hpm 2.5, hpm 10, time\n") 26 | for linenum, line in enumerate(in_file): # Fine lines of relevant data 27 | if line.lower().find(substr) != -1: # If case-insensitive substring search matches, then: 28 | hpm_occur.append((linenum, line.rstrip('\n'))) # strip linebreaks, store line and line number in list as tuple. 29 | ts = find_between(line, "ts", ",") # Find timestamp data 30 | hpm2 = find_between(line, "[", ",") # Find 2.5 HPM data 31 | hpm10 = find_between(line, "hpm", "}") 32 | hpm10 = find_between(hpm10, ",", "]") # Find 10 HPM data 33 | ts = ts.replace(":",'') 34 | ts = ts.replace('"','') 35 | 36 | if ((int(ts) - 7) % 600 == 0): 37 | if ((int(hpm2) < hpmMAX) & (int(hpm10) < hpmMAX)): 38 | t = time.strftime('%m-%d %H:%M', time.localtime(int(ts))) 39 | row = ts + ',' + hpm2 + ',' + hpm10 + ',' + t + '\n' 40 | csv.write(row) -------------------------------------------------------------------------------- /LogParsers/readHPM10.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pylab as plt 2 | import time 3 | 4 | 5 | # 10 HPM DATA 6 | 7 | hpm_occur = [] 8 | data = {} # The dictionary where we will store results. 9 | substr = "hpm" # Substring to use for search. 10 | hpmMAX = 40 # Upper limit of feasible HPM readings 11 | #logfile = 'idle.txt' # Log file to read 12 | logfile = input("Please input logfile: ") 13 | #csvfile = 'idleHPM10.csv' # CSV file to write data to 14 | csvfile = input("Please input CSV file to write to: ") 15 | 16 | def find_between(s, first, last): 17 | try: 18 | start = s.index(first) + len(first) 19 | end = s.index(last, start) 20 | return s[start:end] 21 | except ValueError: 22 | return '' 23 | 24 | 25 | 26 | with open (logfile, 'rt') as in_file: # Open file for reading text. 27 | csv = open(csvfile, "w") # )pen CSV file to record log 28 | csv.write("Timestamp, hpm , time\n") 29 | for linenum, line in enumerate(in_file): # Fine lines of relevant data 30 | if line.lower().find(substr) != -1: # If case-insensitive substring search matches, then: 31 | hpm_occur.append((linenum, line.rstrip('\n'))) # strip linebreaks, store line and line number in list as tuple. 32 | ts = find_between(line, "ts", ",") # Find timestamp data 33 | hpm = find_between(line, "hpm", "}") 34 | hpm = find_between(hpm, ",", "]") # Find 10 HPM data 35 | ts = ts.replace(":",'') 36 | ts = ts.replace('"','') 37 | if ((int(ts) - 7) % 600 == 0): 38 | if (int(hpm) < hpmMAX): 39 | data[ts] = hpm 40 | t = time.strftime('%m-%d %H:%M', time.localtime(int(ts))) 41 | row = ts + ',' + hpm + ',' + t + '\n' 42 | csv.write(row) 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /LogParsers/readHPM2.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pylab as plt 2 | import time 3 | 4 | 5 | # 2.5 HPM DATA 6 | 7 | hpm_occur = [] 8 | data = {} # The dictionary where we will store results. 9 | substr = "hpm" # Substring to use for search. 10 | hpmMAX = 40 # Upper limit of feasible HPM readings 11 | #logfile = 'idle.txt' # Log file to read 12 | logfile = input("Please input logfile: ") 13 | #csvfile = 'idleHPM10.csv' # CSV file to write data to 14 | csvfile = input("Please input CSV file to write to: ") 15 | 16 | def find_between(s, first, last): 17 | try: 18 | start = s.index(first) + len(first) 19 | end = s.index(last, start) 20 | return s[start:end] 21 | except ValueError: 22 | return '' 23 | 24 | 25 | 26 | with open (logfile, 'rt') as in_file: # Open file for reading text. 27 | csv = open(csvfile, "w") # Open CSV file to record log 28 | csv.write("Timestamp, hpm , time\n") 29 | for linenum, line in enumerate(in_file): # Fine lines of relevant data 30 | if line.lower().find(substr) != -1: # If case-insensitive substring search matches, then: 31 | hpm_occur.append((linenum, line.rstrip('\n'))) # strip linebreaks, store line and line number in list as tuple. 32 | ts = find_between(line, "ts", ",") # Find timestamp data 33 | hpm = find_between(line, "[", ",") # Find 2.5 HPM data 34 | ts = ts.replace(":",'') 35 | ts = ts.replace('"','') 36 | if ((int(ts) - 7) % 600 == 0): 37 | if (int(hpm) < hpmMAX): 38 | data[ts] = hpm 39 | t = time.strftime('%m-%d %H:%M', time.localtime(int(ts))) 40 | row = ts + ',' + hpm + ',' + t + '\n' 41 | csv.write(row) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Knight Lab SensorGridPM 2 | 3 | Wireless particulate matter monitoring over LoRa networks 4 | 5 | This is a version of the SensorGrid project that is specific to monitoring 6 | particulate matter data (PM 2.5 and PM 10) and collecting that data over a 7 | LoRa wireless network. 8 | 9 | 10 | ## Development 11 | 12 | This code is structured for development in PlatformIO with its entry point set 13 | to enable Arduino IDE compilation as well. See below for information about 14 | compiling with the Arduino IDE. 15 | 16 | ### Dependencies 17 | 18 | See the `lib_deps` section of the `platformio.ini` file 19 | 20 | ### Arduino IDE 21 | 22 | Development is mostly done on PlatformIO (https://platformio.org/) but we do 23 | occasional checks to ensure deployment on Arduino IDE. It should just work. 24 | 25 | ### Code Style 26 | 27 | Please lint with cpplint before submitting code changes: 28 | 29 | See this project's `CPPLINT.cfg` file for details about flags used for linting. 30 | ``` 31 | cpplint /* 32 | ``` 33 | 34 | We are making an effort to move toward the Google C++ style guide: 35 | https://google.github.io/styleguide/cppguide.html 36 | 37 | with some notable exceptions: 38 | 39 | #### include subdirs not required 40 | 41 | The requirement for specifying subdirs adds unnecessary complexity to project 42 | maintenance. We use a relatively flat code structure within a given project, 43 | thus includes will either be in the form `"file.h"` where file.h is in the 44 | same directory or `` where Library is accessible on the compile path. 45 | 46 | As a result, please lint with `--filter=-build/include_subdir` 47 | 48 | 49 | #### naming conventions 50 | 51 | * Variables should be `snake_case` 52 | * Functions and methods should be `camelCase` (Not PascalCase) 53 | -------------------------------------------------------------------------------- /SensorGridLoRaCollector/config.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Northwestern University 3 | */ 4 | #include "config.h" 5 | 6 | struct Config config; 7 | 8 | int SAMPLE_PERIOD = 5; // minutes 9 | 10 | /** 11 | * We pull both pins 8 and 19 HIGH during SD card read. The default configuration 12 | * on the integrated LoRa M0 uses pin 8 for RFM95 chip select, but the WiFi 13 | * collector module with a LoRa wing uses pin 19 as the chip select. Since we have 14 | * not yet read the config file, it could be either one. 15 | * 16 | * No other RFM95 CS alternates are supported at this time 17 | */ 18 | void loadConfig() { 19 | int default_rfm_cs = atoi(DEFAULT_RFM95_CS); 20 | int alt_rfm_cs = atoi(ALTERNATE_RFM95_CS); 21 | digitalWrite(default_rfm_cs, HIGH); 22 | digitalWrite(alt_rfm_cs, HIGH); 23 | if (!readSDConfig(CONFIG_FILE)) { 24 | digitalWrite(default_rfm_cs, LOW); 25 | digitalWrite(alt_rfm_cs, LOW); 26 | config.network_id = (uint32_t)(atoi(getConfig("NETWORK_ID"))); 27 | config.node_id = (uint32_t)(atoi(getConfig("NODE_ID"))); 28 | config.rf95_freq = static_cast(atof(getConfig("RF95_FREQ"))); 29 | config.tx_power = (uint8_t)(atoi(getConfig("TX_POWER"))); 30 | config.sensorgrid_version = (uint8_t)atoi( 31 | getConfig("SENSORGRID_VERSION", DEFAULT_SENSORGRID_VERSION)); 32 | config.log_file = getConfig("LOGFILE", DEFAULT_LOG_FILE); 33 | config.display_timeout = (uint32_t)(atoi( 34 | getConfig("DISPLAY_TIMEOUT", DEFAULT_DISPLAY_TIMEOUT))); 35 | config.node_type = (uint8_t)(atoi(getConfig("NODE_TYPE"))); 36 | config.collection_period = (uint32_t)(atoi( 37 | getConfig("COLLECTION_PERIOD", DEFAULT_COLLECTION_PERIOD))); 38 | 39 | /* WiFi collector configs */ 40 | config.wifi_ssid = getConfig("WIFI_SSID"); 41 | config.wifi_password = getConfig("WIFI_PASSWORD"); 42 | config.api_host = getConfig("API_HOST"); 43 | config.api_port = (uint16_t)(atoi( 44 | getConfig("API_PORT", DEFAULT_API_PORT))); 45 | 46 | /* radio and SD card pinouts */ 47 | config.SD_CHIP_SELECT_PIN = (uint32_t)(atoi( 48 | getConfig("SD_CHIP_SELECT_PIN", DEFAULT_SD_CHIP_SELECT_PIN))); 49 | config.RFM95_CS = (uint8_t)(atoi( 50 | getConfig("RFM95_CS", DEFAULT_RFM95_CS))); 51 | config.RFM95_RST = (uint8_t)(atoi( 52 | getConfig("RFM95_RST", DEFAULT_RFM95_RST))); 53 | config.RFM95_INT = (uint8_t)(atoi( 54 | getConfig("RFM95_INT", DEFAULT_RFM95_INT))); 55 | 56 | /* Node IDs on collector */ 57 | // char *node_ids_str[MAX_NODES] = {0}; 58 | // config.node_ids[MAX_NODES] = {0}; 59 | } else { 60 | logln(F("ERROR: No config file found")); 61 | fail(FAIL_CODE_CONFIG_READ); 62 | } 63 | logln(F("Config loaded")); 64 | if (!config.node_id) { 65 | logln(F("ERROR: Missing required config parameter NODE_ID")); 66 | fail(FAIL_CODE_BAD_CONFIG); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /SensorGridLoRaCollector/config.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Northwestern University 3 | */ 4 | #ifndef KNIGHTLAB_SENSORGRID_SENSORGRIDPM_CONFIG_H_ 5 | #define KNIGHTLAB_SENSORGRID_SENSORGRIDPM_CONFIG_H_ 6 | 7 | //#include 8 | //#include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /** 15 | * SensorGrid will not print to serial if USB is not attached. This can be 16 | * problematic when debugging timing sensitive issues, in which case set 17 | * ALWAYS_LOG to true 18 | */ 19 | #define ALWAYS_LOG false 20 | #define DO_STANDBY 1 21 | 22 | enum Mode { WAIT, INIT, SAMPLE, HEARTBEAT, COMMUNICATE, STANDBY }; 23 | extern enum Mode mode; 24 | 25 | extern void _setup(); 26 | extern void _loop(); 27 | 28 | extern RTCZero rtcz; 29 | //extern RTC_PCF8523 rtc; 30 | extern uint32_t get_time(); 31 | // extern OLED oled; 32 | 33 | #define FAIL_CODE_GENERAL 1 34 | #define FAIL_CODE_BAD_CONFIG 2 35 | #define FAIL_CODE_CONFIG_READ 3 36 | 37 | #define NODE_TYPE_COLLECTOR 1 38 | #define NODE_TYPE_SENSOR 2 39 | 40 | //#define MAX_NODES 20 41 | 42 | #define CONFIG_FILE "CONFIG.TXT" // Adalogger doesn't seem to like underscores 43 | // in the name!!! 44 | 45 | /* Config defaults are strings so they can be passed to getConfig */ 46 | // char* DEFAULT_NETWORK_ID = "1"; 47 | #define DEFAULT_NETWORK_ID "1" 48 | #define DEFAULT_RF95_FREQ "915.0" // for U.S. 49 | #define DEFAULT_TX_POWER "13" 50 | #define DEFAULT_SENSORGRID_VERSION "1" 51 | #define DEFAULT_DISPLAY_TIMEOUT "60" 52 | #define DEFAULT_LOG_FILE "sensorgrid.log" 53 | #define DEFAULT_COLLECTION_PERIOD "60" // defaults to 60 sec 54 | #define DEFAULT_SD_CHIP_SELECT_PIN "10" 55 | #define DEFAULT_RFM95_CS "8" 56 | #define ALTERNATE_RFM95_CS "19" 57 | #define DEFAULT_RFM95_RST "4" 58 | #define DEFAULT_RFM95_INT "3" 59 | #define DEFAULT_API_PORT "80" 60 | 61 | 62 | struct Config { 63 | uint32_t network_id; 64 | uint32_t node_id; 65 | float rf95_freq; 66 | uint8_t tx_power; 67 | uint8_t sensorgrid_version; 68 | char* log_file; 69 | char* log_mode; 70 | uint32_t display_timeout; 71 | uint8_t node_type; 72 | int node_ids[255]; 73 | uint32_t collection_period; // in seconds 74 | uint32_t SD_CHIP_SELECT_PIN; 75 | uint32_t RFM95_CS; 76 | uint32_t RFM95_RST; 77 | uint32_t RFM95_INT; 78 | 79 | /* wifi collector */ 80 | char* wifi_ssid; 81 | char* wifi_password; 82 | char* api_host; 83 | uint16_t api_port; 84 | }; 85 | 86 | extern void loadConfig(); 87 | extern struct Config config; 88 | extern int SAMPLE_PERIOD; 89 | 90 | struct Message { 91 | uint8_t sensorgrid_version; 92 | uint8_t network_id; 93 | uint8_t from_node; 94 | uint8_t message_type; 95 | uint8_t len; 96 | char data[100]; 97 | } __attribute__((packed)); 98 | 99 | #endif // KNIGHTLAB_SENSORGRID_SENSORGRIDPM_CONFIG_H_ 100 | -------------------------------------------------------------------------------- /SensorGridLoRaCollector/wifi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "wifi.h" 3 | 4 | static WiFiClient client; 5 | //WiFiSSLClient client; 6 | 7 | static bool wifi_present = true; 8 | 9 | WiFiClient wifiClient() 10 | { 11 | return client; 12 | } 13 | 14 | void printWiFiStatus() 15 | { 16 | println("SSID: %s; IP: %s; RSSI: %l", WiFi.SSID(), WiFi.localIP(), WiFi.RSSI()); 17 | } 18 | 19 | void connectWiFi(char ssid[], char pass[], char host[], int port) 20 | { 21 | println("Setting pins CS: %d; IRQ: %d; RST: %d; EN: %d", 22 | WIFI_CS, WIFI_IRQ, WIFI_RST, WIFI_EN); 23 | WiFi.setPins(WIFI_CS, WIFI_IRQ, WIFI_RST, WIFI_EN); 24 | int status = WL_IDLE_STATUS; 25 | while (status!= WL_CONNECTED) { 26 | Serial.print("Attempting to connect to SSID: "); 27 | Serial.println(ssid); 28 | Serial.print("Using password: "); 29 | Serial.println(pass); 30 | status = WiFi.begin(ssid, pass); 31 | Serial.println("Connected to WiFi"); 32 | Serial.print("\nStarting connection "); 33 | Serial.print(host); Serial.print(":"); Serial.println(port); 34 | if (client.connect(host, port)) { 35 | Serial.println("connected to server"); 36 | } else { 37 | Serial.println("server connection failed"); 38 | } 39 | } 40 | Serial.println(".. connected"); 41 | } 42 | 43 | void disconnectWiFi() 44 | { 45 | client.stop(); 46 | WiFi.end(); 47 | } 48 | 49 | /* 50 | void reconnectClient(WiFiClient &client, char* ssid) 51 | { 52 | client.stop(); 53 | Serial.print("Reconnecting to "); 54 | Serial.print(config.api_host); 55 | Serial.print(":"); 56 | Serial.println(config.api_port); 57 | if (client.connect(config.api_host, config.api_port)) { 58 | Serial.println("connecting ..."); 59 | } else { 60 | Serial.println("Failed to reconnect"); 61 | } 62 | } 63 | */ 64 | 65 | byte printWiFi(char* s) 66 | { 67 | return client.print(s); 68 | } 69 | 70 | byte printlnWiFi(char* s) 71 | { 72 | return client.println(s); 73 | } 74 | 75 | byte printWiFi(int i) 76 | { 77 | return client.print(i); 78 | } 79 | 80 | byte printlnWiFi(int i) 81 | { 82 | return client.println(i); 83 | } 84 | 85 | void receiveWiFiResponse() 86 | { 87 | Serial.println("Receiving server response .."); 88 | while (!client.available()) {} 89 | while (client.available()) { 90 | char c = client.read(); 91 | Serial.write(c); 92 | } 93 | println("********** done"); 94 | } 95 | 96 | void readHeader() 97 | { 98 | bool blank_line = true; 99 | while(client.available()) { 100 | char c = client.read(); 101 | Serial.print(c); 102 | if (c == '\n') { 103 | if (blank_line) { 104 | Serial.println("--"); 105 | Serial.println("Done reading header."); 106 | break; 107 | } 108 | blank_line = true; 109 | } else if (c != '\r') { 110 | blank_line = false; 111 | } 112 | } 113 | } 114 | 115 | void receiveWiFiResponse(char buffer[], size_t len) 116 | { 117 | // TODO: see https://arduinojson.org/v6/example/http-client/ 118 | Serial.println("Receiving buffered server response .."); 119 | while (!client.available()) {} 120 | readHeader(); 121 | int i = 0; 122 | while (i < len - 1 && client.available()) { 123 | char c = client.read(); 124 | buffer[i++] = c; 125 | Serial.write(c); 126 | } 127 | buffer[i] = '\0'; 128 | Serial.print("i: "); 129 | Serial.println(i); 130 | Serial.print("buffer: "); 131 | Serial.println(buffer); 132 | println("********** done"); 133 | } 134 | -------------------------------------------------------------------------------- /SensorGridLoRaCollector/wifi.h: -------------------------------------------------------------------------------- 1 | #ifndef KNIGHTLAB_WIFI 2 | #define KNIGHTLAB_WIFI 3 | 4 | #include 5 | #define WIFI_CS 8 6 | #define WIFI_IRQ 7 7 | #define WIFI_RST 4 8 | #define WIFI_EN 2 9 | 10 | void printWiFiStatus(); 11 | void connectWiFi(char ssid[], char pass[], char host[], int port); 12 | void disconnectWiFi(); 13 | byte printWiFi(char* s); 14 | byte printlnWiFi(char* s); 15 | byte printWiFi(int i); 16 | byte printlnWiFi(int i); 17 | void receiveWiFiResponse(); 18 | void receiveWiFiResponse(char buffer[], size_t len); 19 | #endif -------------------------------------------------------------------------------- /SensorGridPM/SensorGridPM.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * Knight Lab SensorGrid 3 | * 4 | * Wireless air pollution (Particulate Matter PM2.5 & PM10) monitoring over LoRa radio 5 | * 6 | * Copyright 2018 Northwestern University 7 | */ 8 | //#include 9 | #include "config.h" 10 | #include "runtime.h" 11 | #include 12 | #include 13 | 14 | #define SET_CLOCK false 15 | #define SERIAL_TIMEOUT 10000 16 | 17 | WatchdogType Watchdog; 18 | 19 | RTC_PCF8523 rtc; 20 | int start_epoch; 21 | OLED oled = OLED(rtc); 22 | 23 | /* local utilities */ 24 | 25 | 26 | static void setupLogging() { 27 | Serial.begin(115200); 28 | set_logging(true); 29 | } 30 | 31 | static void setRTC() { 32 | DateTime dt = DateTime(start_epoch); 33 | rtc.adjust(DateTime(start_epoch - 18000)); // Subtract 5 hours to adjust for timezone 34 | } 35 | 36 | static void setRTCz() { 37 | DateTime dt = rtc.now(); 38 | rtcz.setDate(dt.day(), dt.month(), dt.year()-2000); // When setting the year to 2018, it becomes 34 39 | rtcz.setTime(dt.hour(), dt.minute(), dt.second()); 40 | } 41 | 42 | static void printCurrentTime() { 43 | DateTime dt = rtc.now(); 44 | logln(F("Current time: %02d:%02d:%02d, "), rtcz.getHours(), rtcz.getMinutes(), rtcz.getSeconds()); 45 | logln(F("Current Epoch: %u"), rtcz.getEpoch()); 46 | } 47 | 48 | /* end local utilities */ 49 | 50 | /* 51 | * interrupts 52 | */ 53 | 54 | void aButton_ISR() { 55 | static bool busy = false; 56 | if (busy) return; 57 | busy = true; 58 | // rtcz.disableAlarm(); 59 | static volatile int state = 0; 60 | state = !digitalRead(BUTTON_A); 61 | if (state) { 62 | logln(F("A-Button pushed")); 63 | oled.toggleDisplayState(); 64 | } 65 | if (oled.isOn()) { 66 | //updateClock(); // Temporarily removed 67 | oled.displayDateTime(); 68 | } else { 69 | oled.clear(); 70 | } 71 | busy = false; 72 | // rtcz.disableAlarm(); 73 | /* 74 | aButtonState = !digitalRead(BUTTON_A); 75 | if (aButtonState) { 76 | _on = !_on; 77 | if (_on) { 78 | _activated_time = millis(); 79 | displayDateTimeOLED(); 80 | } else { 81 | standbyOLED(); 82 | } 83 | } 84 | */ 85 | } 86 | 87 | /* 88 | void updateClock() { 89 | int gps_year = GPS.year; 90 | if (gps_year != 0 && gps_year != 80) { 91 | uint32_t gps_time = DateTime(GPS.year, GPS.month, GPS.day, GPS.hour, GPS.minute, GPS.seconds).unixtime(); 92 | uint32_t rtc_time = rtc.now().unixtime(); 93 | if (rtc_time - gps_time > 1 || gps_time - rtc_time > 1) { 94 | rtc.adjust(DateTime(GPS.year, GPS.month, GPS.day, GPS.hour, GPS.minute, GPS.seconds)); 95 | } 96 | } 97 | setRTCz(); 98 | } 99 | */ 100 | 101 | void setupSensors() { 102 | pinMode(12, OUTPUT); // enable pin to HPM boost 103 | Serial.println("Loading sensor config ..."); 104 | loadSensorConfig(nodeId(), getTime); 105 | Serial.println(".. done loading sensor config"); 106 | } 107 | 108 | void setupClocks() { 109 | rtc.begin(); 110 | /* In general, we no longer use SET_CLOCK. Instead use a GPS module to set the time */ 111 | if (SET_CLOCK) { 112 | log_(F("Printing initial DateTime: ")); 113 | rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 114 | log_(F(__DATE__)); 115 | log_(F(' ')); 116 | logln(F(__TIME__)); 117 | } 118 | rtcz.begin(); 119 | setRTCz(); 120 | } 121 | 122 | /* hard fault handler */ 123 | 124 | /* see Segger debug manual: https://www.segger.com/downloads/application-notes/AN00016 */ 125 | static volatile unsigned int _Continue; 126 | void HardFault_Handler(void) { 127 | _Continue = 0u; 128 | // 129 | // When stuck here, change the variable value to != 0 in order to step out 130 | // 131 | logln(F("!!!!**** HARD FAULT -- REQUESTING RESET *****!!!!")); 132 | SCB->AIRCR = 0x05FA0004; // System reset 133 | while (_Continue == 0u) {} 134 | } 135 | 136 | // Doesn't include watchdog like the one in runtime 137 | static void standbyTEMP() { 138 | if (DO_STANDBY) { 139 | rtcz.standbyMode(); 140 | } 141 | } 142 | // Doesn't include interrupt function like in runtime 143 | void setInterruptTimeoutTEMP(DateTime &datetime) { 144 | rtcz.setAlarmSeconds(datetime.second()); 145 | rtcz.setAlarmMinutes(datetime.minute()); 146 | rtcz.enableAlarm(rtcz.MATCH_MMSS); 147 | } 148 | 149 | void waitSerial() 150 | { 151 | unsigned long _start = millis(); 152 | while ( !Serial && (millis() - _start) < SERIAL_TIMEOUT ) {} 153 | } 154 | 155 | void setup() { 156 | waitSerial(); 157 | rtcz.begin(); 158 | systemStartTime(rtcz.getEpoch()); 159 | Watchdog.enable(); 160 | config.loadConfig(); 161 | nodeId(config.node_id); 162 | setupSensors(); 163 | setupLoRa(config.RFM95_CS, config.RFM95_RST, config.RFM95_INT); 164 | setupRunner(); 165 | for (uint8_t i=0; i<255; i++) { 166 | txPower(i, DEFAULT_LORA_TX_POWER); 167 | } 168 | Watchdog.disable(); 169 | recordRestart(); 170 | } 171 | 172 | void loop() { 173 | Watchdog.enable(); 174 | runRunner(); 175 | 176 | /** 177 | * Some apparent lockups may actually be indefinite looping on STANDBY status 178 | * if for some reason the scheduled interrupt never happens to kick us out of 179 | * STANDBY mode. For that reason, the standby_timer will track time and should 180 | * never get to be longer than the scheduled heartbeat period. 181 | */ 182 | //static uint32_t standby_timer = getTime(); 183 | } 184 | -------------------------------------------------------------------------------- /SensorGridPM/config.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Northwestern University 3 | */ 4 | #include "config.h" 5 | #include 6 | 7 | struct Config config; 8 | 9 | uint32_t getTime() { 10 | return rtcz.getEpoch(); 11 | } 12 | 13 | /** 14 | * We pull both pins 8 and 19 HIGH during SD card read. The default configuration 15 | * on the integrated LoRa M0 uses pin 8 for RFM95 chip select, but the WiFi 16 | * collector module with a LoRa wing uses pin 19 as the chip select. Since we have 17 | * not yet read the config file, it could be either one. 18 | * 19 | * No other RFM95 CS alternates are supported at this time 20 | */ 21 | 22 | bool Base::readConfig() { 23 | if (!readSDConfig(CONFIG_FILE)){ 24 | file_read = true; 25 | return true; 26 | } 27 | return false; 28 | } 29 | 30 | bool WifiConfig::loadConfig() { 31 | if (file_read || Base::readConfig()) { 32 | wifi_ssid = getConfig("WIFI_SSID"); 33 | wifi_password = getConfig("WIFI_PASSWORD"); 34 | api_host = getConfig("API_HOST"); 35 | api_port = (uint16_t)(atoi( 36 | getConfig("API_PORT", DEFAULT_API_PORT))); 37 | return true; 38 | } else { 39 | logln(F("ERROR: No config file found")); 40 | return false; 41 | } 42 | } 43 | 44 | bool RadioConfig::loadConfig() { 45 | if (file_read || Base::readConfig()) { 46 | SD_CHIP_SELECT_PIN = (uint32_t)(atoi( 47 | getConfig("SD_CHIP_SELECT_PIN", DEFAULT_SD_CHIP_SELECT_PIN))); 48 | RFM95_CS = (uint32_t)(atoi( 49 | getConfig("RFM95_CS", DEFAULT_RFM95_CS))); 50 | RFM95_RST = (uint32_t)(atoi( 51 | getConfig("RFM95_RST", DEFAULT_RFM95_RST))); 52 | RFM95_INT = (uint32_t)(atoi( 53 | getConfig("RFM95_INT", DEFAULT_RFM95_INT))); 54 | return true; 55 | } else { 56 | SD_CHIP_SELECT_PIN = (uint32_t)(atoi(DEFAULT_SD_CHIP_SELECT_PIN)); 57 | RFM95_CS = (uint32_t)(atoi(DEFAULT_RFM95_CS)); 58 | RFM95_RST = (uint32_t)(atoi(DEFAULT_RFM95_RST)); 59 | RFM95_INT = (uint32_t)(atoi(DEFAULT_RFM95_INT)); 60 | logln(F("ERROR: No config file found")); 61 | return false; 62 | } 63 | } 64 | 65 | bool Config::loadConfig() { 66 | int default_rfm_cs = atoi(DEFAULT_RFM95_CS); 67 | int alt_rfm_cs = atoi(ALTERNATE_RFM95_CS); 68 | digitalWrite(default_rfm_cs, HIGH); 69 | digitalWrite(alt_rfm_cs, HIGH); 70 | if (file_read || Base::readConfig()) { 71 | digitalWrite(default_rfm_cs, LOW); 72 | digitalWrite(alt_rfm_cs, LOW); 73 | network_id = (uint32_t)(atoi(getConfig("NETWORK_ID"))); 74 | node_id = (uint8_t)(atoi(getConfig("NODE_ID", "0"))); 75 | collector_id = (uint8_t)(atoi(getConfig("COLLECTOR_ID"))); 76 | rf95_freq = static_cast(atof(getConfig("RF95_FREQ"))); 77 | tx_power = (uint8_t)(atoi(getConfig("TX_POWER"))); 78 | sensorgrid_version = (uint8_t)atoi( 79 | getConfig("SENSORGRID_VERSION", DEFAULT_SENSORGRID_VERSION)); 80 | log_file = getConfig("LOGFILE", DEFAULT_LOG_FILE); 81 | display_timeout = (uint32_t)(atoi( 82 | getConfig("DISPLAY_TIMEOUT", DEFAULT_DISPLAY_TIMEOUT))); 83 | node_type = (uint8_t)(atoi(getConfig("NODE_TYPE"))); 84 | heartbeat_period = (uint32_t)(atoi( 85 | getConfig("HEARTBEAT_PERIOD", DEFAULT_HEARTBEAT_PERIOD))); 86 | sample_period = (uint32_t)(atoi( 87 | getConfig("SAMPLE_PERIOD", DEFAULT_SAMPLE_PERIOD))); 88 | collection_period = (uint32_t)(atoi( 89 | getConfig("COLLECTION_PERIOD", DEFAULT_COLLECTION_PERIOD))); 90 | } 91 | Serial.print("Loading LoRa config .."); 92 | RadioConfig::loadConfig(); 93 | Serial.println(".. done"); 94 | if (node_id == 0) { 95 | Serial.println("Node ID not configured. Using Lower byte of Si7021 serial number A."); 96 | Adafruit_Si7021 _sensor = Adafruit_Si7021(); 97 | _sensor.begin(); 98 | node_id = (byte)_sensor.sernum_a; 99 | Serial.print("NODE ID: "); 100 | Serial.println(node_id); 101 | } 102 | if (node_id == 0) { 103 | Serial.println("Unable to set Node ID. Check configuration or Si7021 connectivity."); 104 | while(1); 105 | } 106 | return true; 107 | } -------------------------------------------------------------------------------- /SensorGridPM/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Northwestern University 3 | */ 4 | #ifndef SENSORGRIDPM_CONFIG_H_ 5 | #define SENSORGRIDPM_CONFIG_H_ 6 | 7 | // #include 8 | //#include 9 | //#include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "oled.h" 15 | // #include 16 | 17 | /* 18 | * SensorGrid will not print to serial if USB is not attached. This can be 19 | * problematic when debugging timing sensitive issues, in which case set 20 | * ALWAYS_LOG to true 21 | */ 22 | #define ALWAYS_LOG false 23 | #define DO_STANDBY true 24 | #define DO_TRANSMIT_DATA false 25 | #define DO_LOG_DATA true 26 | #define INIT_LEAD_TIME 7 27 | #define MESSAGE_DATA_SIZE 100 28 | #define DATASAMPLE_DATASIZE MESSAGE_DATA_SIZE - 2 29 | #define MAX_LOGFILE_AGE 2592000 // 30 days in seconds 30 | 31 | // comment 32 | 33 | //enum Mode { WAIT, INIT, SAMPLE, HEARTBEAT, COMMUNICATE, STANDBY, WAIT_FOR_BATTERY }; 34 | //extern enum Mode mode; 35 | 36 | extern void _setup(); 37 | extern void _loop(); 38 | extern OLED oled; 39 | 40 | // Task callback functions 41 | extern void initSensors(); 42 | extern void readDataSamples(); 43 | extern void logData(); 44 | 45 | 46 | //extern RTCZero rtcz; 47 | extern RTC_PCF8523 rtc; 48 | extern uint32_t get_time(); 49 | extern long getNextTaskTEMP(); 50 | extern WatchdogSAMD Watchdog; 51 | 52 | #define FAIL_CODE_GENERAL 1 53 | #define FAIL_CODE_BAD_CONFIG 2 54 | #define FAIL_CODE_CONFIG_READ 3 55 | 56 | #define NODE_TYPE_COLLECTOR 1 57 | #define NODE_TYPE_SENSOR 2 58 | 59 | #define MAX_NODES 20 60 | 61 | #define CONFIG_FILE "CONFIG.TXT" // Adalogger doesn't seem to like underscores 62 | // in the name!!! 63 | 64 | /* Config defaults are strings so they can be passed to getConfig */ 65 | // char* DEFAULT_NETWORK_ID = "1"; 66 | #define DEFAULT_NETWORK_ID "1" 67 | #define DEFAULT_RF95_FREQ "915.0" // for U.S. 68 | #define DEFAULT_TX_POWER "13" 69 | #define DEFAULT_SENSORGRID_VERSION "1" 70 | #define DEFAULT_DISPLAY_TIMEOUT "60" 71 | #define DEFAULT_LOG_FILE "sensorgrid.log" 72 | #define DEFAULT_HEARTBEAT_PERIOD "30" // defaults to 30 seconds 73 | #define DEFAULT_SAMPLE_PERIOD "600" // defaults to 10 minutes 74 | #define DEFAULT_COLLECTION_PERIOD "3600" // defaults to 60 minutes 75 | #define DEFAULT_SD_CHIP_SELECT_PIN "10" 76 | #define DEFAULT_RFM95_CS "8" 77 | #define ALTERNATE_RFM95_CS "19" 78 | #define DEFAULT_RFM95_RST "4" 79 | #define DEFAULT_RFM95_INT "3" 80 | #define DEFAULT_API_PORT "80" 81 | 82 | extern struct SensorConfig *sensor_config_head; 83 | 84 | struct Base { 85 | bool file_read = false; 86 | bool readConfig(); 87 | }; 88 | 89 | struct WifiConfig : virtual public Base { 90 | char* wifi_ssid; 91 | char* wifi_password; 92 | char* api_host; 93 | uint16_t api_port; 94 | 95 | bool loadConfig(); 96 | }; 97 | 98 | struct RadioConfig : virtual public Base { 99 | uint8_t SD_CHIP_SELECT_PIN; 100 | uint32_t RFM95_CS; 101 | uint32_t RFM95_RST; 102 | uint32_t RFM95_INT; 103 | 104 | bool loadConfig(); 105 | }; 106 | 107 | struct Config : public WifiConfig, public RadioConfig { 108 | uint32_t network_id; 109 | uint8_t node_id; 110 | uint8_t collector_id; 111 | float rf95_freq; 112 | uint8_t tx_power; 113 | uint8_t sensorgrid_version; 114 | char* log_file; 115 | char* log_mode; 116 | uint32_t display_timeout; 117 | uint8_t node_type; 118 | int node_ids[255]; 119 | uint32_t heartbeat_period; // in seconds 120 | uint32_t sample_period; // in minutes 121 | uint32_t collection_period; // in minutes 122 | 123 | bool loadConfig(); 124 | }; 125 | 126 | extern uint32_t getTime(); 127 | extern void loadConfig(); 128 | extern struct Config config; 129 | 130 | struct Message { 131 | uint8_t sensorgrid_version; 132 | uint8_t network_id; 133 | uint8_t from_node; 134 | uint8_t message_type; 135 | uint8_t len; 136 | char data[100]; 137 | } __attribute__((packed)); 138 | 139 | typedef bool (*SensorStartFunction)(); 140 | typedef size_t (*SensorReadFunction)(char *buf, int len); 141 | typedef bool (*SensorStopFunction)(); 142 | 143 | #endif // SENSORGRIDPM_CONFIG_H_ 144 | -------------------------------------------------------------------------------- /SensorGridPM/oled.cpp: -------------------------------------------------------------------------------- 1 | #include "oled.h" 2 | 3 | static uint8_t last_minute; 4 | char last_datestring[17]; 5 | #define DATE_STRING_SIZE 17 6 | 7 | /* 8 | static RTC_PCF8523 _rtc; 9 | static Adafruit_FeatherOLED _oled; 10 | static bool _on; 11 | static uint32_t _activated_time; 12 | static volatile int aButtonState; 13 | 14 | 15 | void displayDateTimeOLED() 16 | { 17 | if (!_on) return; 18 | DateTime now = _rtc.now(); 19 | if (now.minute() == last_minute) return; 20 | _oled.fillRect(0, 8, 16*6, 7, BLACK); 21 | _oled.setTextColor(WHITE); 22 | sprintf(last_datestring, "%d-%02d-%02d %02d:%02d", now.year(), now.month(), 23 | now.day(), now.hour(), now.minute()); 24 | _oled.setCursor(0,8); 25 | _oled.print(last_datestring); 26 | _oled.display(); 27 | last_minute = now.minute(); 28 | } 29 | 30 | void clearOLED() 31 | { 32 | _oled.clearDisplay(); 33 | _oled.display(); 34 | } 35 | 36 | void standbyOLED() 37 | { 38 | clearOLED(); 39 | _on = false; 40 | } 41 | 42 | void aButton_ISR() 43 | { 44 | aButtonState = !digitalRead(BUTTON_A); 45 | if (aButtonState) { 46 | _on = !_on; 47 | if (_on) { 48 | _activated_time = millis(); 49 | displayDateTimeOLED(); 50 | } else { 51 | standbyOLED(); 52 | } 53 | } 54 | } 55 | 56 | void initOLED(RTC_PCF8523 &rtc) 57 | { 58 | _rtc = rtc; 59 | _oled.init(); 60 | attachInterrupt(BUTTON_A, aButton_ISR, CHANGE); 61 | _activated_time = millis(); 62 | } 63 | */ 64 | 65 | OLED::OLED(RTC_PCF8523 &rtc) { 66 | _rtc = rtc; 67 | Adafruit_FeatherOLED _oled = Adafruit_FeatherOLED(); 68 | _on = false; 69 | aButtonState = 0; 70 | // _oled.init(); 71 | // pinMode(BUTTON_A, INPUT_PULLUP); // we may be having conflicts with this button 72 | // pinMode(BUTTON_C, INPUT_PULLUP); 73 | // display.setBatteryIcon(true); 74 | // display.setBattery(batteryLevel()); 75 | // display.renderBattery(); 76 | // displayCurrentRTCDateTime(); 77 | // displayID(); 78 | } 79 | 80 | bool OLED::isOn() { 81 | return _on; 82 | } 83 | 84 | void OLED::displayDateTime(bool force_refresh) { 85 | if (!_on) return; 86 | DateTime now = _rtc.now(); 87 | if (force_refresh || now.minute() != last_minute) { 88 | _oled.fillRect(0, 8, 16*6, 7, BLACK); 89 | _oled.setTextColor(WHITE); 90 | snprintf(last_datestring, DATE_STRING_SIZE, "%d-%02d-%02d %02d:%02d", now.year(), now.month(), 91 | now.day(), now.hour(), now.minute()); 92 | _oled.setCursor(0, 8); 93 | _oled.print(last_datestring); 94 | _oled.display(); 95 | last_minute = now.minute(); 96 | } 97 | } 98 | 99 | void OLED::init() { 100 | _oled.init(); 101 | clear(); 102 | _on = true; 103 | // attachInterrupt(BUTTON_A, aButton_ISR, CHANGE); 104 | _activated_time = millis(); 105 | } 106 | 107 | void OLED::clear() { 108 | _oled.clearDisplay(); 109 | _oled.display(); 110 | } 111 | 112 | void OLED::standby() { 113 | clear(); 114 | _on = false; 115 | } 116 | 117 | // void OLED::setButtonFunction(uint32_t pin, void (*fcn)(void), int state) 118 | void OLED::setButtonFunction(uint32_t pin, voidFuncPtr fcn, uint32_t mode) { 119 | attachInterrupt(pin, fcn, mode); 120 | } 121 | 122 | void OLED::on() { 123 | _oled.ssd1306_command(SSD1306_DISPLAYON); 124 | _on = true; 125 | } 126 | 127 | void OLED::off() { 128 | _oled.ssd1306_command(SSD1306_DISPLAYOFF); 129 | _on = false; 130 | } 131 | 132 | void OLED::toggleDisplayState() { 133 | _on = !_on; 134 | if (_on) { 135 | on(); 136 | } else { 137 | off(); 138 | } 139 | } 140 | 141 | void OLED::displayStartup() { 142 | _oled.clearDisplay(); 143 | _oled.setTextSize(2); 144 | _oled.print("KnightLab SensorGrid"); 145 | _oled.display(); 146 | } 147 | 148 | void OLED::endDisplayStartup() { 149 | _oled.clearDisplay(); 150 | _oled.setTextSize(1); 151 | _oled.display(); 152 | } 153 | -------------------------------------------------------------------------------- /SensorGridPM/oled.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSORGRIDPM_OLED_H_ 2 | #define SENSORGRIDPM_OLED_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #define BUTTON_A 9 8 | 9 | // extern void initOLED(RTC_PCF8523 &rtc); 10 | // extern void displayDateTimeOLED(); 11 | // extern void clear(); 12 | // extern void standby(); 13 | 14 | class OLED { 15 | RTC_PCF8523 _rtc; 16 | Adafruit_FeatherOLED _oled; 17 | bool _on; 18 | uint32_t _activated_time; 19 | volatile int aButtonState; 20 | 21 | public: 22 | OLED(RTC_PCF8523 &rtc); 23 | void displayDateTime(bool force_refresh = false); 24 | void init(); 25 | void clear(); 26 | void standby(); 27 | // void setButtonFunction(int button, void (*fcn)(void), int state); 28 | void setButtonFunction(uint32_t pin, voidFuncPtr fcn, uint32_t mode); 29 | void on(); 30 | void off(); 31 | bool isOn(); 32 | void toggleDisplayState(); 33 | void displayStartup(); 34 | void endDisplayStartup(); 35 | }; 36 | 37 | 38 | #endif // SENSORGRIDPM_OLED_H_ 39 | -------------------------------------------------------------------------------- /SensorGridPM/runtime.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Northwestern University 3 | */ 4 | #ifndef SENSORGRIDPM_RUNTIME_H_ 5 | #define SENSORGRIDPM_RUNTIME_H_ 6 | 7 | // #include 8 | #include 9 | 10 | static SdFat SD; 11 | 12 | 13 | //extern void setInitTimeout(); 14 | // extern void initSensors(); 15 | //extern void setSampleTimeout(); 16 | extern void recordDataSamples(); 17 | //extern void setCommunicateDataTimeout(); 18 | //extern void flashHeartbeat(); 19 | extern void flashHeartbeatOn(); 20 | extern void flashHeartbeatOff(); 21 | extern void recordBatteryLevel(); 22 | extern void recordRestart(); 23 | //extern void recordTempAndHumidity(); 24 | extern void recordUptime(); 25 | // extern void logData(bool clear); 26 | //extern void transmitData(bool clear); 27 | //extern uint32_t getNextCollectionTime(); 28 | // extern void readDataSamples(); 29 | extern bool checkBatteryLevel(); 30 | void setupRunner(); 31 | void runRunner(); 32 | 33 | /* data history */ 34 | struct DataSample { 35 | char data[DATASAMPLE_DATASIZE]; 36 | struct DataSample *next; 37 | }; 38 | 39 | #endif // SENSORGRIDPM_RUNTIME_H_ 40 | -------------------------------------------------------------------------------- /ardcomm.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import time 3 | 4 | ser = serial.Serial('/dev/cu.usbmodem1411', 115200) 5 | #ser = serial.Serial('/dev/cu.usbmodem1411', 9600) 6 | 7 | ts = time.time() 8 | ts = int(ts) 9 | ts = str(ts) 10 | ts = 't' + ts 11 | 12 | ser.write(ts.encode()) 13 | -------------------------------------------------------------------------------- /cad/HPMFeatherWing/HPMFeatherWing.pro: -------------------------------------------------------------------------------- 1 | update=22/05/2015 07:44:53 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [pcbnew] 9 | version=1 10 | LastNetListRead= 11 | UseCmpFile=1 12 | PadDrill=0.600000000000 13 | PadDrillOvalY=0.600000000000 14 | PadSizeH=1.500000000000 15 | PadSizeV=1.500000000000 16 | PcbTextSizeV=1.500000000000 17 | PcbTextSizeH=1.500000000000 18 | PcbTextThickness=0.300000000000 19 | ModuleTextSizeV=1.000000000000 20 | ModuleTextSizeH=1.000000000000 21 | ModuleTextSizeThickness=0.150000000000 22 | SolderMaskClearance=0.000000000000 23 | SolderMaskMinWidth=0.000000000000 24 | DrawSegmentWidth=0.200000000000 25 | BoardOutlineThickness=0.100000000000 26 | ModuleOutlineThickness=0.150000000000 27 | [cvpcb] 28 | version=1 29 | NetIExt=net 30 | [eeschema] 31 | version=1 32 | LibDir= 33 | [eeschema/libraries] 34 | LibName1=power 35 | LibName2=device 36 | LibName3=transistors 37 | LibName4=conn 38 | LibName5=linear 39 | LibName6=regul 40 | LibName7=74xx 41 | LibName8=cmos4000 42 | LibName9=adc-dac 43 | LibName10=memory 44 | LibName11=xilinx 45 | LibName12=microcontrollers 46 | LibName13=dsp 47 | LibName14=microchip 48 | LibName15=analog_switches 49 | LibName16=motorola 50 | LibName17=texas 51 | LibName18=intel 52 | LibName19=audio 53 | LibName20=interface 54 | LibName21=digital-audio 55 | LibName22=philips 56 | LibName23=display 57 | LibName24=cypress 58 | LibName25=siliconi 59 | LibName26=opto 60 | LibName27=atmel 61 | LibName28=contrib 62 | LibName29=valves 63 | -------------------------------------------------------------------------------- /legacy/ADAFRUIT_SI1145.cpp: -------------------------------------------------------------------------------- 1 | #include "ADAFRUIT_SI1145.h" 2 | 3 | /* Adafruit Si1145 IR/UV/Visible light breakout */ 4 | 5 | namespace ADAFRUIT_SI1145 { 6 | 7 | static Adafruit_SI1145 sensor = Adafruit_SI1145(); 8 | 9 | bool setup() 10 | { 11 | Serial.print(F("Si1145 ")); 12 | if (sensor.begin()) { 13 | Serial.println(F("Found")); 14 | return true; 15 | } else { 16 | Serial.println(F("Not Found")); 17 | return false; 18 | } 19 | } 20 | 21 | int32_t readVisible() 22 | { 23 | return (int32_t)sensor.readVisible(); 24 | } 25 | 26 | int32_t readIR() 27 | { 28 | return (int32_t)sensor.readIR(); 29 | } 30 | 31 | int32_t readUV() 32 | { 33 | return (int32_t)(sensor.readUV()*100); 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /legacy/ADAFRUIT_SI1145.h: -------------------------------------------------------------------------------- 1 | #ifndef ADAFRUIT_SI1145_H 2 | #define ADAFRUIT_SI1145_H 3 | 4 | #include 5 | 6 | namespace ADAFRUIT_SI1145 { 7 | bool setup(); 8 | int32_t readVisible(); 9 | int32_t readIR(); 10 | int32_t readUV(); 11 | } 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /legacy/ADAFRUIT_SI7021.cpp: -------------------------------------------------------------------------------- 1 | #include "ADAFRUIT_SI7021.h" 2 | 3 | /* Adafruit Si7021 temperature/humidity breakout */ 4 | 5 | namespace ADAFRUIT_SI7021 { 6 | 7 | static Adafruit_Si7021 sensor = Adafruit_Si7021(); 8 | 9 | bool setup() 10 | { 11 | Serial.print(F("Si7021 ")); 12 | if (sensor.begin()) { 13 | Serial.println(F("Found")); 14 | return true; 15 | } else { 16 | Serial.println(F("Not Found")); 17 | return false; 18 | } 19 | } 20 | 21 | int32_t readTemperature() 22 | { 23 | return (int32_t)(sensor.readTemperature()*100); 24 | } 25 | 26 | int32_t readHumidity() 27 | { 28 | return (int32_t)(sensor.readHumidity()*100); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /legacy/ADAFRUIT_SI7021.h: -------------------------------------------------------------------------------- 1 | #ifndef ADAFRUIT_SI7021_H 2 | #define ADAFRUIT_SI7021_H 3 | 4 | #include 5 | 6 | namespace ADAFRUIT_SI7021 { 7 | bool setup(); 8 | int32_t readTemperature(); 9 | int32_t readHumidity(); 10 | } 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /legacy/ADAFRUIT_ULTIMATE_GPS.cpp: -------------------------------------------------------------------------------- 1 | #include "ADAFRUIT_ULTIMATE_GPS.h" 2 | 3 | /* Adafruit Ultimate GPS FeatherWing */ 4 | 5 | namespace ADAFRUIT_ULTIMATE_GPS { 6 | 7 | /* GPS is a global */ 8 | 9 | bool setup() 10 | { 11 | setupGPS(); 12 | return true; // TODO: can we determine if GPS is really setup? 13 | } 14 | 15 | int32_t fix() { 16 | return GPS.fix; 17 | } 18 | 19 | int32_t satellites() { 20 | return GPS.satellites; 21 | } 22 | 23 | int32_t satfix() { 24 | if (GPS.fix) return GPS.satellites; 25 | return -1 * GPS.satellites; 26 | } 27 | 28 | int32_t latitudeDegrees() { 29 | return (int32_t)(roundf(GPS.latitudeDegrees * 1000)); 30 | } 31 | 32 | int32_t longitudeDegrees() { 33 | return (int32_t)(roundf(GPS.longitudeDegrees * 1000)); 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /legacy/ADAFRUIT_ULTIMATE_GPS.h: -------------------------------------------------------------------------------- 1 | #ifndef ADAFRUIT_ULTIMATE_GPS_H 2 | #define ADAFRUIT_ULTIMATE_GPS_H 3 | 4 | #include 5 | 6 | namespace ADAFRUIT_ULTIMATE_GPS { 7 | bool setup(); 8 | int32_t fix(); 9 | int32_t satellites(); 10 | int32_t satfix(); 11 | int32_t latitudeDegrees(); 12 | int32_t longitudeDegrees(); 13 | } 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /legacy/BUILD.md: -------------------------------------------------------------------------------- 1 | # SensorGrid Building Guide 2 | 3 | Be sure to take a look at the Planning Guide first. Most relevant information is there. For actual Feather and FeatherWing assembly, please see the Adafruit tutorial: 4 | https://learn.adafruit.com/adafruit-feather-m0-basic-proto/assembly 5 | 6 | 7 | ## Additional notes/tips: 8 | 9 | * Keep your stack planning in mind. Desoldering is much less fun than soldering. If you're not sure what kind of header to use, go with a stacking header for maximum flexibility. 10 | 11 | * When soldering stacking headers, the breadboard trick shown in the Adafruit tutorial does not work so well. Some things that help to keep the headers straight: 12 | - tack-solder one or two pins and double-check the alignment before doing all the pins 13 | - it can help to use an already soldered module as a plug-in base to get the spacing and alignment right 14 | - once a pin or two are soldered, it can be helpful to use the vice. Probably not before that though 15 | - mostly, I just try to stand my headers up straight and hope for the best. They don't always turn out the greatest though 16 | 17 | * If you are still planning things out, consider wire wrapping instead of soldering where you can get away with it. Not so much for the Feather headers, but, e.g. if you are planning out a new sensor module 18 | 19 | * Some additional prototyping tools that might come in handy: 20 | - breadboard and jumper wires 21 | - breadboard terminal blocks 22 | - specialized Feather terminal (https://www.adafruit.com/products/3173) 23 | - specialized Feather terminal board (https://www.adafruit.com/products/2926) 24 | 25 | # Tutorial: Build a SensorGrid Network with Ordered Node Collection Configuration 26 | 27 | Ordered node collection is one of two supported routing/data collection schemes supported by SensorGrid. While it requires the additional configuration of an ordered list of sensor nodes on the collector, dedicated routers are not required, making it a more straightforward approach to deployment. Furthermore, ordered nodes can shut off their radios between requests, significantly extending battery life. 28 | 29 | This tutorial will detail the steps for creating a SensorGrid Network with an ordered node configuration. Nodes can either be gas/dust sensors, or light (visible/uv/ir)/humidity/temperature sensors -- both types are covered here. To simplify, sensor components may be eliminated from this tutorial at will. E.g. one could build merely a gas sensor, or simply a light sensor. 30 | 31 | Due to space limitations on the protoboard, a larger configuration would be needed to accomodate all of the sensors covered. This could be done by using a tripler base in lieu of a doubler, making the required room for all components. 32 | 33 | ## Build the collector node 34 | 35 | ### Components 36 | Adafruit Feather short stacking headers 37 | Adafruit M0 LoRa Feather 38 | Adafruit Adalogger Feather Wing 39 | 40 | ## Build the sensor nodes 41 | 42 | Note: this doubler configuration will accomodate a single sensor module. To include both modules on a node, use a tripler instead of a doubler. 43 | 44 | ### Components 45 | Adafruit proto doubler (or tripler) 46 | Adafruit Feather short stacking headers 47 | Adafruit M0 LoRa Feather 48 | Adafruit Adalogger Feather Wing 49 | Adafruit OLED Feather Wing 50 | 51 | 52 | ## Build the air quality / dust module 53 | 54 | ### Components 55 | 56 | Adafruit proto 57 | Adafruit Feather male headers 58 | Sharp dust sensor 59 | Seeed studio Grove Air Quality sensor 60 | 61 | ## Build the light / temp / humidity module 62 | 63 | ### Components 64 | 65 | Adafruit proto 66 | Adafruit Feather male headers 67 | Adafruit SI1145 light/UV/IR sensor 68 | Adafruit SI7021 temperature/humidity sensor 69 | -------------------------------------------------------------------------------- /legacy/COLLECTOR.md: -------------------------------------------------------------------------------- 1 | # SensorGrid data collector nodes 2 | 3 | A SensorGrid data collector is responsible for collecting data from the nodes in the network and posting that data to the API. 4 | 5 | Two collector types are supported: 6 | 7 | 1. Serial Collector 8 | 2. WiFi Collector 9 | 10 | ## Serial Collector 11 | 12 | A Serial Collector is simply a SensorGrid node configured as a collector and connected to a computer serial port. The node itself handles data acquisition from sensor nodes. Code (TBD) running on the computer parses log lines from the node's serial output and posts the data to the API. This approach could work well, for example, with a Raspberry Pi setup to handle the serial data. 13 | 14 | The standard modular configuration of a serial collector is simply to stack a LoRa M0 Feather with an Adalogger Featherwing and configure the node as a collector (NODE_TYPE 4) 15 | 16 | 17 | ## WiFi Collector 18 | 19 | A Wifi Collector handles both the collection of data and the posting of data to the API, via WiFi of course. 20 | 21 | It is important to note: in order to handle pin conflict issues, the modular configuration of a WiFi collector is different from other nodes. The WiFi collector requires the following physical stack (note particularly the use of a LoRa wing rather than LoRa core module): 22 | 23 | * Adalogger Featherwing https://www.adafruit.com/product/2922 24 | * LoRa Featherwing https://www.adafruit.com/product/3231 25 | * WiFi M0 Feather (WINC1500) https://www.adafruit.com/product/3010 26 | 27 | The LoRa CS, RST, and INT pins will need to be wired. The typical connections are: 28 | 29 | * CS 19 (flyout pin F) 30 | * RST 11 (flyout pin A) 31 | * INT 6 (flyout pin D) 32 | 33 | The collector configuration will require settings for the alternative pinouts, for the API endpoint, and for WiFi connectivity. An example configuration file follows: 34 | 35 | ``` 36 | PROTOCOL_VERSION 1 37 | NODE_ID 1 38 | RF95_FREQ 915.0 39 | RFM95_CS 19 40 | RFM95_RST 11 41 | RFM95_INT 6 42 | TX_POWER 10 43 | LOGFILE sensorgrid.log 44 | DISPLAY 0 45 | NODE_TYPE 4 46 | ORDERED_NODE_IDS 2,3,4 47 | COLLECTION_PERIOD 60 48 | WIFI_SSID YourWifiSSID 49 | WIFI_PASSWORD YourWifiPassword 50 | API_HOST API.HOST.IP.ADDRESS 51 | API_PORT 80 52 | ``` 53 | 54 | Notes: 55 | 56 | * You will need to set values for WIFI_SSID, WIFI_PASSWORD, and API_HOST 57 | * WIFI_SSID currently cannot contain spaces in the name (to be fixed soon) 58 | * API_PORT is configurable and defaults to 80 59 | * Note that DNS is not currently supported, so an IP address is required for API_HOST 60 | 61 | -------------------------------------------------------------------------------- /legacy/CONFIG.TXT: -------------------------------------------------------------------------------- 1 | VERSION 1 2 | NETWORK_ID 1 3 | NODE_ID 1 4 | RF95_FREQ 915.0 5 | TX_POWER 10 6 | DISPLAY 1 7 | WIFI_SSID Your SSID 8 | WIFI_PASS YourPassword 9 | API_SERVER your.api.ip.address 10 | API_PORT YourApiPort 11 | API_HOST YourApiHost 12 | LOGFILE sensorgrid.log 13 | DATA_3 SI7021_TEMP 14 | DATA_4 SI7021_HUMIDITY 15 | DATA_5 SI1145_VIS 16 | DATA_6 SI1145_IR 17 | DATA_7 SI1145_UV 18 | DATA_8 SHARP_GP2Y1010AU0F_DUST 19 | DATA_9 FAKE_9 20 | -------------------------------------------------------------------------------- /legacy/CONFIGURATIONS.md: -------------------------------------------------------------------------------- 1 | # SensorGrid Configurations 2 | 3 | ## Modular configurations 4 | 5 | Choose your configuration! You should do this before you start soldering things since the configuration will impact header choices. There are a lot of options. If in doubt, solder stacking headers onto everything and figure out your configuration as you go along -- however, judicious header choices can make for a more compact and nicer looking product! 6 | 7 | Every node requires a LoRa Feather and an Adalogger FeatherWing. All other components of a node depend on your requirements. Use the configuration tables below to match configurations to your requirements. 8 | 9 | ### Sensor options 10 | 11 | We have built, or are currently actively developing, support for the following sensor options: 12 | 13 | * GPS 14 | * Temperature/humidity 15 | * Light (Visible/UV/IR) 16 | * air-quality/dust (coming soon!) 17 | 18 | TODO: one or more temp/hum/light boards seem possibly fried. Need to check this out be sure we don't have compatibility issues with this board 19 | 20 | ### Single-stack configurations 21 | 22 | * No extra proto boards required 23 | * Good for collector and repeater nodes 24 | * Either/or OLED/Sensor top-component 25 | * OLED+Sensor can be used if sensor does not need to be on top 26 | 27 | | | 1a | 2a | 3a | 4a | 28 | |-------|--------|--------|--------|--------| 29 | | Stack | | | | OLED | 30 | | | | OLED | Sensor | Sensor | 31 | | | Logger | Logger | Logger | Logger | 32 | | | LoRa | LoRa | LoRa | LoRa | 33 | 34 | 1a. Single-stacked (Communicative)\* Senseless Logger 35 | 36 | Requirements: 37 | * LoRa 38 | * Logger 39 | 40 | Uses: 41 | * A simple way to get started just testing out the protocol 42 | * Can be used as a collector and/or repeater node 43 | * Coupled with one or more #3 nodes provides the simplest SensorGrid configuration for logging remote sensor data 44 | 45 | \* Note: all nodes, as currently specified, are communicative nodes. There is a currently unsupported use-case for non-communicative nodes that simply log their own data. Support for non-communicative logger nodes is pending for future development tracks, depending on perceived demand 46 | 47 | 48 | 2a. Single-stacked Senseless Logger with Readout 49 | 50 | Requirements: 51 | * LoRa 52 | * Logger 53 | * OLED 54 | 55 | Uses: 56 | * Adds a readout display to the simplest setup of #1 57 | * Currently only displays time (more info TBD) 58 | 59 | 60 | 3a. Single-stacked Sensing Logger 61 | 62 | Requirements: 63 | * LoRa 64 | * Logger 65 | * Sensor 66 | 67 | Uses: 68 | * The simplest node that is actually a sensor node 69 | * Current support for temperature, humidity, light (Visible, UV, IR) 70 | * Dust/air quality support coming soon! 71 | 72 | 73 | 4a. Single-stacked Sensing Logger with Readout 74 | 75 | NOTE: This configuration is not recommended for many sensor types. Only use if your sensor does not need to be top-stack 76 | 77 | Requirements: 78 | * LoRa 79 | * Logger 80 | * Sensor 81 | * OLED 82 | 83 | Uses: 84 | * A compact sensing communicative logger for use with sensors that do not need to be at the top of the stack 85 | * Can be used with temperature or humidity sensors 86 | * Single stacked OLED configuration will probably be compatible with the dust sensor (since the dust sensor itself will not be part of the stacked configuration) but TBD to be certain 87 | * Not recommended for use with light sensors 88 | 89 | 90 | 91 | ### Double-stack configurations (requires Adafruit doubler proto https://www.adafruit.com/products/2890) 92 | 93 | With minimal additional soldering, the Feather doubler proto board provides much more flexibility than single-stacked configurations. For configurations that require multiple top-stack components, a dble or triple configuration is essential. 94 | 95 | There are several doubler configurations that are operationally identical to single-stack configurations. The doubler provides a wider, flatter configuration which may be desirable. 96 | 97 | 98 | | | 1b | 2b | 3b | 4b | 99 | |-------|----------------|----------------|----------------|----------------| 100 | | Stack | | OLED | Sensor | OLED -- Sensor | 101 | | | LoRa -- Logger | LoRa -- Logger | LoRa -- Logger | LoRa -- Logger | 102 | 103 | 1b - 3b requirements and usages are identally to their single-stacked counterparts (see 1a-3a above) 104 | 105 | 4b. 106 | Requirements: (see #4b above) 107 | 108 | Uses: 109 | * See #4a above 110 | * Also compatible with sensors required to be top-stack, e.g.: light sensors 111 | 112 | 113 | | | 5b | 6b | 114 | |-------|-----------------|-----------------| 115 | | | | OLED | 116 | | Stack | Sensor - Sensor | Sensor - Sensor | 117 | | | LoRa -- Logger | LoRa -- Logger | 118 | 119 | 120 | 5b. 121 | Requirements: 122 | * LoRa 123 | * Logger 124 | * Sensors 125 | 126 | Uses: 127 | * Multiple top-stack sensors (e.g. a light sensor and GPS) 128 | * This is the simplest configuration for using GPS with an additional top-stack sensor 129 | 130 | 6b. 131 | Requirements: 132 | * LoRa 133 | * Logger 134 | * Sensors 135 | * OLED 136 | 137 | Uses: 138 | * Multiple sensors where a readout is desired and at least one sensor does not need to be top-stack (e.g. GPS + temp/humidity) 139 | 140 | ### 1-node 141 | ### 2-node time-only SensorGrid 142 | -------------------------------------------------------------------------------- /legacy/DEPLOYMENT.md: -------------------------------------------------------------------------------- 1 | # SensorGrid Deployment 2 | 3 | ## Power 4 | 5 | Nodes can be powered by a USB source (e.g. by plugging into a computer USB port), or more commonly by a 3.7v lithium ion polymer battery (e.g. https://www.adafruit.com/product/1578, https://www.adafruit.com/product/258, https://www.adafruit.com/product/328) 6 | 7 | Important: Strain relief is strongly recommended with these batteries. The solder connections to the batteries are very fragile and will break if the wires are not constrained from movement. 8 | 9 | If USB power is provided with a battery attached, the battery will recharge. However, this recharge will be slow since the node is also being powered. Thus, it is recommended to use a dedicated charger such as Adafruit's USB Lilon/LiPoly charger (https://www.adafruit.com/product/259) or the versitile USB/DC/Solar Lithium Ion/Polymer charger - v2 (https://www.adafruit.com/product/390) 10 | 11 | Please use only a single battery attached to a node via the provided battery connector. Improper use of LiPoly batteries can be unsafe. For more information, see Adafruit's excellent introduction to LiPoly batteries: 12 | https://learn.adafruit.com/li-ion-and-lipoly-batteries?view=all 13 | 14 | ## Configuration 15 | 16 | SD card should have a root-level CONFIG.txt file with key-value configurations and the following restrictions: 17 | 18 | - key-value pairs are each on a distinct line in the file 19 | - key-values are separated by a single space 20 | - no trailing whitespace. no blank lines 21 | - no commenting format is currently supported 22 | 23 | ### Currently supported configuration options 24 | 25 | The following configuration options are available 26 | 27 | **PROTOCOL_VERSION** 1 28 | 29 | 1 is currently the only supported version number 30 | 31 | **NODE_ID** (1 - 255) 32 | 33 | **RF95_FREQ** 915.0 34 | 35 | Defaults to 915.0. This is currently the only supported radio frequency. For operation outside the US, please check your local ordinances to see if a different frequency is required. This may require a different radio module than the specified module. 36 | 37 | **TX_POWER** (5 - 23) 38 | 39 | Radio tranmission power. Higher power for greater distances but with battery life tradeoff. Use the lowest power that is practical for your application. 40 | 41 | **DISPLAY** (1 or 0) 42 | 43 | Set to 1 if you have an OLED FeatherWing attached. Otherwise 0 44 | 45 | **LOGFILE** sensorgrid.log 46 | 47 | The logfile used to log events according to the LOGMODE setting 48 | 49 | **DISPLAY_TIMEOUT** 60 50 | 51 | OLED display timeout in seconds. Defaults to 60 52 | 53 | **NODE_TYPE** (1 - 5) 54 | 55 | _Standard routing (requires dedicated router nodes)_ 56 | 57 | 1 COLLECTOR 58 | 59 | 2 ROUTER 60 | 61 | 3 SENSOR 62 | 63 | _Ordered collection routing_ 64 | 65 | 4 ORDERED_COLLECTOR 66 | 67 | 5 ORDERED_SENSOR_ROUTER 68 | 69 | **COLLECTOR_ID** 70 | 71 | The ID of the collector this node sends data to (required for standard routing only) 72 | 73 | **ORDERED_NODE_IDS** 74 | 75 | Required only on an ordered collector node. Ordered comma-delimited list of nodes to collect from. (Ordered collection routing only). Nodes should be ordered from the outside edge of the network toward the center in order to ensure network connectivity as ordered nodes will sleep after transmitting data. 76 | 77 | **DATA_0** .. **DATA_9** 78 | 79 | Data registers to be configured with named data types below 80 | 81 | The following data register data types are supported: 82 | 83 | - GPS_FIX 84 | - GPS_SATS 85 | - GPS_SATFIX 86 | - GPS_LAT_DEG 87 | - GPS_LON_DEG 88 | - GROVE_AIR_QUALITY_1_3 89 | - SHARP_GP2Y1010AU0F_DUST 90 | - SI1145_IR 91 | - SI1145_UV 92 | - SI1145_VIS 93 | - SI7021_HUMIDITY 94 | - SI7021_TEMP 95 | 96 | 97 | ### Currently unsupported configuration options 98 | 99 | Legacy, deprecated, or temporarily removed pending implementation 100 | 101 | **NETWORK_ID** 102 | 103 | **WIFI_SSID** 104 | 105 | Your SSID (Not currently used) 106 | 107 | **WIFI_PASS** 108 | 109 | YourPassword (Not currently used) 110 | 111 | **API_SERVER** 112 | 113 | your.api.ip.address (Not currently used) 114 | 115 | **API_PORT** 116 | 117 | YourApiPort (Not currently used) 118 | 119 | **API_HOST** 120 | 121 | YourApiHost (Not currently used) 122 | 123 | **LOGMODE** 124 | 125 | Determines which events are logged to the SD card. Not currently used 126 | 127 | 128 | -------------------------------------------------------------------------------- /legacy/Datalogger/Datalogger.ino: -------------------------------------------------------------------------------- 1 | #include "SHARP_GP2Y1010AU0F.h" 2 | #include "RTClib.h" 3 | #include 4 | #include 5 | #include "RTClib.h" 6 | #include 7 | #include 8 | 9 | #define VBATPIN 9 10 | #define DEFAULT_SD_CHIP_SELECT_PIN 10 //4 for Uno 11 | #define ALTERNATE_RFM95_CS 19 //10 for Uno 12 | #define DEFAULT_RFM95_CS 8 //not needed for Uno 13 | 14 | static bool USE_WATCHDOG = true; 15 | 16 | /* sensor configs */ 17 | //log out clock time -> use GPS 18 | uint8_t SHARP_GP2Y1010AU0F_DUST_PIN = A0; //was A3 for Uno 19 | 20 | /* real time clock */ 21 | RTC_PCF8523 rtc; 22 | bool setClock = true; 23 | char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; 24 | int nodeId = 1; 25 | 26 | 27 | float batteryLevel() 28 | { 29 | float measuredvbat = analogRead(VBATPIN); 30 | measuredvbat *= 2; // we divided by 2, so multiply back 31 | measuredvbat *= 3.3; // Multiply by 3.3V, our reference voltage 32 | measuredvbat /= 1024; // convert to voltage 33 | return measuredvbat; 34 | } 35 | 36 | bool init_sd() 37 | { 38 | digitalWrite(DEFAULT_SD_CHIP_SELECT_PIN, HIGH); 39 | digitalWrite(DEFAULT_RFM95_CS, HIGH); 40 | digitalWrite(ALTERNATE_RFM95_CS, HIGH); 41 | Serial.print("Initializing SD card..."); 42 | if (!SD.begin(DEFAULT_SD_CHIP_SELECT_PIN)) { 43 | digitalWrite(ALTERNATE_RFM95_CS, LOW); 44 | digitalWrite(DEFAULT_SD_CHIP_SELECT_PIN, LOW); 45 | Serial.println("Card failed, or not present"); 46 | return false; 47 | } 48 | Serial.println("card initialized."); 49 | return true; 50 | } 51 | 52 | void setup() { 53 | // Open serial communications and wait for port to open: 54 | //while (!Serial); 55 | //Serial.begin(115200); 56 | if (!init_sd()) { while(1); } 57 | 58 | /* Sharp GP2Y1010AU0F dust */ 59 | if (SHARP_GP2Y1010AU0F::setup(SHARP_GP2Y1010AU0F_DUST_PIN)) { //setup to pin A0 60 | Serial.println("Setting up sensors..."); 61 | } 62 | else { 63 | Serial.println("Dust sensor setup failed"); 64 | } 65 | 66 | /* RTC Clock */ 67 | if (!rtc.begin()) { 68 | Serial.println("Error: Failed to initialize RTC"); 69 | } 70 | if (setClock) { 71 | Serial.print("Printing initial DateTime: "); 72 | rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); 73 | Serial.print(F(__DATE__)); 74 | Serial.print('/'); 75 | Serial.println(F(__TIME__)); 76 | } 77 | } 78 | 79 | static long last_data_sample = 0; 80 | void loop() { 81 | long diff = rtc.now().secondstime() - last_data_sample; 82 | if (diff < 30) { 83 | if (USE_WATCHDOG) { 84 | int sleepMS = Watchdog.sleep(30 - diff); // sleep up to the remainder of the period 85 | } 86 | return; 87 | } 88 | last_data_sample = rtc.now().secondstime(); 89 | //digitalWrite(LED_BUILTIN, HIGH); 90 | Serial.print("Printing node number: "); 91 | Serial.println(nodeId); 92 | File dataFile = SD.open("datalog.csv", FILE_WRITE); 93 | if (dataFile) { 94 | Serial.println("Opening dataFile..."); 95 | } else { 96 | Serial.println("error opening datalog.csv"); 97 | } 98 | DateTime now = rtc.now(); 99 | Serial.print(now.year(), DEC); 100 | Serial.print('/'); 101 | Serial.print(now.month(), DEC); 102 | Serial.print('/'); 103 | Serial.print(now.day(), DEC); 104 | Serial.print(" ("); 105 | Serial.print(daysOfTheWeek[now.dayOfTheWeek()]); 106 | Serial.print(") "); 107 | Serial.print(now.hour(), DEC); 108 | Serial.print(':'); 109 | Serial.print(now.minute(), DEC); 110 | Serial.print(':'); 111 | Serial.print(now.second(), DEC); 112 | Serial.println(); 113 | dataFile.print(now.year(), DEC); 114 | dataFile.print("-"); 115 | dataFile.print(now.month(), DEC); 116 | dataFile.print("-"); 117 | dataFile.print(now.day(), DEC); 118 | dataFile.print("T"); 119 | dataFile.print(now.hour(), DEC); 120 | dataFile.print(":"); 121 | dataFile.print(now.minute(), DEC); 122 | dataFile.print(":"); 123 | dataFile.print(now.second(), DEC); 124 | dataFile.print(","); 125 | dataFile.print(batteryLevel(), DEC); 126 | dataFile.print(","); 127 | static int dust_sense_vo_measured = 0; 128 | static float dust_sense_calc_voltage = 0; 129 | static float dust_density = 0; 130 | digitalWrite(DUST_SENSOR_LED_POWER, LOW); 131 | delayMicroseconds(DUST_SENSOR_SAMPLING_TIME); 132 | dust_sense_vo_measured = analogRead(SHARP_GP2Y1010AU0F_DUST_PIN); 133 | delayMicroseconds(DUST_SENSOR_DELTA_TIME); 134 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_OFF); 135 | delayMicroseconds(DUST_SENSOR_SLEEP_TIME); 136 | dust_sense_calc_voltage = dust_sense_vo_measured * (DUST_SENSOR_VCC / 1024); 137 | dust_density = 0.17 * dust_sense_calc_voltage - 0.1; 138 | Serial.print(F("Raw Signal Value (0-1023): ")); 139 | Serial.print(dust_sense_vo_measured, DEC); 140 | dataFile.print(dust_sense_vo_measured, DEC); 141 | Serial.print(F(" - Voltage: ")); 142 | dataFile.print(","); 143 | Serial.print(dust_sense_calc_voltage); 144 | dataFile.print(dust_sense_calc_voltage); 145 | Serial.print(F(" - Dust Density: ")); 146 | Serial.println(dust_density); 147 | dataFile.println(dust_density); 148 | dataFile.close(); 149 | Serial.println("Closing dataFile"); 150 | digitalWrite(LED_BUILTIN, HIGH); 151 | Watchdog.sleep(500); 152 | digitalWrite(LED_BUILTIN, LOW); 153 | } 154 | 155 | -------------------------------------------------------------------------------- /legacy/Datalogger/SHARP_GP2Y1010AU0F.cpp: -------------------------------------------------------------------------------- 1 | /* Adapted from: 2 | http://arduinodev.woofex.net/2012/12/01/standalone-sharp-dust-sensor/ 3 | & http://www.howmuchsnow.com/arduino/airquality/ 4 | */ 5 | #include "SHARP_GP2Y1010AU0F.h" 6 | 7 | namespace SHARP_GP2Y1010AU0F { 8 | 9 | static uint8_t _data_pin = 0; 10 | 11 | bool setup(uint8_t data_pin) 12 | { 13 | if (!data_pin) { 14 | Serial.println(F("Sharp GP2Y1010AU0F not configured")); 15 | return false; 16 | } 17 | _data_pin = data_pin; 18 | Serial.print(F("Setting Sharp GP2Y1010AU0F data pin to: ")); 19 | Serial.println(_data_pin, DEC); 20 | pinMode(DUST_SENSOR_LED_POWER, OUTPUT); 21 | pinMode(_data_pin, INPUT); 22 | return true; 23 | } 24 | 25 | /** 26 | * Legacy read function returns a float value 27 | */ 28 | float read_legacy() 29 | { 30 | if (_data_pin) { 31 | static float dust_sense_vo_measured = 0; 32 | static float dust_sense_calc_voltage = 0; 33 | static float dust_density = 0; 34 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_ON); 35 | delayMicroseconds(DUST_SENSOR_SAMPLING_TIME); 36 | dust_sense_vo_measured = analogRead(_data_pin); 37 | delayMicroseconds(DUST_SENSOR_DELTA_TIME); 38 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_OFF); 39 | delayMicroseconds(DUST_SENSOR_SLEEP_TIME); 40 | dust_sense_calc_voltage = dust_sense_vo_measured * (DUST_SENSOR_VCC / 1024); 41 | dust_density = 0.17 * dust_sense_calc_voltage - 0.1; 42 | Serial.print(F("Raw Signal Value (0-1023): ")); 43 | Serial.print(dust_sense_vo_measured, DEC); 44 | Serial.print(F(" - Voltage: ")); 45 | Serial.print(dust_sense_calc_voltage); 46 | Serial.print(F(" - Dust Density: ")); 47 | Serial.println(dust_density); 48 | return dust_density; 49 | } else { 50 | return 0.0; 51 | } 52 | } 53 | 54 | /** 55 | * Get the analogRead value from the dust data pin 56 | */ 57 | int32_t read() 58 | { 59 | if (_data_pin) { 60 | static float dust_sense_vo_measured = 0; 61 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_ON); 62 | delayMicroseconds(DUST_SENSOR_SAMPLING_TIME); 63 | dust_sense_vo_measured = analogRead(_data_pin); 64 | delayMicroseconds(DUST_SENSOR_DELTA_TIME); 65 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_OFF); 66 | delayMicroseconds(DUST_SENSOR_SLEEP_TIME); 67 | return dust_sense_vo_measured; 68 | } else { 69 | return 0; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /legacy/Datalogger/SHARP_GP2Y1010AU0F.h: -------------------------------------------------------------------------------- 1 | #ifndef SHARP_GP2Y1010AU0F_H 2 | #define SHARP_GP2Y1010AU0F_H 3 | 4 | #include 5 | 6 | #define DUST_SENSOR_LED_POWER 12 7 | #define DUST_SENSOR_SAMPLING_TIME 280 8 | #define DUST_SENSOR_DELTA_TIME 40 9 | #define DUST_SENSOR_SLEEP_TIME 9680 10 | #define DUST_SENSOR_VCC 3.3 11 | #define DUST_SENSOR_LED_ON LOW 12 | #define DUST_SENSOR_LED_OFF HIGH 13 | 14 | namespace SHARP_GP2Y1010AU0F { 15 | void setDataPin(uint8_t pin); 16 | bool setup(uint8_t data_pin); 17 | float read_legacy(); 18 | int32_t read(); 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /legacy/GROVE_AIR_QUALITY_1_3.cpp: -------------------------------------------------------------------------------- 1 | #include "GROVE_AIR_QUALITY_1_3.h" 2 | 3 | /* TODO: do we need to consider historical averages and slope? See tutorial 4 | * at: http://wiki.seeed.cc/Grove-Air_Quality_Sensor_v1.3/ and AirQuality 5 | * library code: https://github.com/SeeedDocument/Grove_Air_Quality_Sensor_v1.3/raw/master/res/AirQuality_Sensor.zip 6 | */ 7 | 8 | namespace GROVE_AIR_QUALITY_1_3 { 9 | 10 | static uint8_t _data_pin = 0; 11 | 12 | bool setup(uint8_t data_pin) 13 | { 14 | if (!data_pin) { 15 | Serial.println(F("Grove Air Quality 1.3 not configured")); 16 | return false; 17 | } 18 | _data_pin = data_pin; 19 | Serial.print(F("Setting Grove Air Quality 1.3 pin to: ")); 20 | Serial.println(_data_pin, DEC); 21 | pinMode(_data_pin, INPUT); 22 | return true; 23 | } 24 | 25 | float read_legacy() 26 | { 27 | if (_data_pin) { 28 | return analogRead(_data_pin) * 3.3 / 1024; 29 | } else { 30 | return 0.0; 31 | } 32 | } 33 | 34 | int32_t read() 35 | { 36 | if (_data_pin) { 37 | return analogRead(_data_pin); 38 | } else { 39 | return 0; 40 | } 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /legacy/GROVE_AIR_QUALITY_1_3.h: -------------------------------------------------------------------------------- 1 | #ifndef GROVE_AIR_QUALITY_H 2 | #define GROVE_AIR_QUALITY_H 3 | 4 | #include 5 | 6 | namespace GROVE_AIR_QUALITY_1_3 { 7 | bool setup(uint8_t data_pin); 8 | float read_legacy(); 9 | int32_t read(); 10 | } 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /legacy/Honeywell/Honeywell.h: -------------------------------------------------------------------------------- 1 | #ifndef _HONEYWELL_H 2 | #define _HONEYWELL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define VBATPIN 9 11 | #define BUTTON_A 9 12 | #define BUTTON_B 6 13 | #define BUTTON_C 5 14 | 15 | extern RTC_PCF8523 rtc; 16 | extern Adafruit_FeatherOLED display; 17 | extern uint32_t display_clock_time; 18 | 19 | uint32_t displayCurrentRTCDateTime(); 20 | void setDate(); 21 | void setupDisplay(); 22 | void displayBatteryLevel(); 23 | void updateDateTimeDisplay(); 24 | void updateGPSDisplay(); 25 | void displayMessage(char* message); 26 | void displayID(); 27 | void displayTx(int toID); 28 | void displayRx(int fromID, float rssi); 29 | void updateDisplayBattery(); 30 | void updateDisplay(); 31 | float batteryLevel(); 32 | 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /legacy/MODULES.md: -------------------------------------------------------------------------------- 1 | # Modules 2 | 3 | ## Feather M0 LoRa (https://www.adafruit.com/products/3178) 4 | 5 | **Required:** Yes 6 | 7 | The core module required on all SensorGrid nodes 8 | 9 | ## Adalogger Featherwing (https://www.adafruit.com/products/2922) 10 | 11 | **Required:** Generally, except for advanced usage 12 | 13 | Used for configuration, logging, and timekeeping. This module is generally 14 | required on all nodes. 15 | 16 | ### Making the logger optional 17 | 18 | It may be possible to elminate the Adalogger from your nodes with some extra work. Note this is advanced usage and is not officially supported. This will necessarily require compiling unique versions of the code to each node. 19 | 20 | The configuration file on the SD card is the key component to making SensorGrid as plug-and-play as possible. For most users, no coding is required to build a SensorGrid. In order to remove the Adalogger as a node requirement, you will need to hardcode configuration values. The easiest way to do this is probably to find the `DFAULT_` values and change them. Note, just depending on the defaults alone will not make for a very useful SensorGrid network. At the very least, you will need to set the ID of each node. 21 | 22 | ## OLED Featherwing (https://www.adafruit.com/products/2900) 23 | 24 | **Required:** No, but strongly recommended (particularly for logging nodes) 25 | 26 | The OLED display shows some useful information including battery level, date/time, and last-message info. In order to save battery life, the display times out and is normally off. Hold the C button to turn it back on. Hold the C button longer to enter shutdown mode before turning off the node. 27 | 28 | The DISPLAY configuration defaults to 0. Be sure to set it to 1 in the config file for the OLED display to work. Note: a node will not work correctly if DISPLAY is set to 1 but no OLED is present. 29 | 30 | Important: There is no available shutdown function if no OLED is intalled and configured. There is a chance of logfile corruption when turning off a node without using the C-button shutdown feature. 31 | -------------------------------------------------------------------------------- /legacy/PLANNING.md: -------------------------------------------------------------------------------- 1 | # SensorGrid Planning Guide 2 | 3 | A little bit of planning will go a long way toward obtaining the parts you really need and building the correct configuration. Better planning docs will become available as we gather better information about SensorGrid's abilities and limitations. 4 | 5 | 6 | ## Plan your network 7 | 8 | A SensorGrid network consists of an ad-hoc deployment of one (actually usually at least two) or more SensorGrid nodes. Each node plays a set of roles which may include: 9 | 10 | * sensor node 11 | * collector 12 | * router 13 | 14 | All nodes are, in effect, routers. So a node is *only* a router if it is not a sensor or collector. A node may be one, two, or all three of these roles. 15 | 16 | ### Sensor nodes 17 | 18 | Sensor nodes collect data from the environment via an integrated sensor module. That data is logged to an SD card and is simultaneously broadcast to the network. 19 | 20 | To be a true "sensor grid" your network will need at least one sensor node. More likely you will have several to be distributed over a local area. 21 | 22 | ### Collector 23 | 24 | A collector may or may not also be a sensor. A collector is such in that, in addition to logging its own data to SD, it is also configured to log data it receives from other nodes. 25 | 26 | * The simplest collector logs all incomming data to the SD card 27 | * A collector may be configured with a WiFi module to write data to a remote API 28 | * Other collector types TBD 29 | 30 | You will probably want at least one collector in your network 31 | 32 | ### Routers 33 | 34 | All nodes are routers. In addition to broadcasting their own data (in the case of sensor nodes) all SensorGrid nodes re-transmit received data, thus re-broadcasting it to the network. 35 | 36 | In some cases you may want additional router nodes that are neither sensors nor collectors. 37 | 38 | ### Plan it! 39 | 40 | (TBD more info about expected ranges, node capacity, etc.) 41 | 42 | To plan your network, fill in the following: 43 | 44 | I will need _ _ _ _ _ sensor nodes (which may also be collectors/repeaters) 45 | 46 | In addition to sensor nodes, I will have _ _ _ _ _ collector(s) that are not sensors 47 | 48 | and _ _ _ _ _ _ repeater(s) that are also not sensors 49 | 50 | 51 | ## Plan your nodes 52 | 53 | For each node you will need to: 54 | 55 | * Plan your layers 56 | * Determine your parts 57 | * Build the modules 58 | * Assemble the node 59 | 60 | ### Plan your layers 61 | 62 | Planning your layers includes: 63 | 64 | 1. Determine top-layer 65 | 2. Determine the base 66 | 3. Choosing headers 67 | 68 | #### 1. Determine top-layer surface level components 69 | 70 | - [ ] OLED digital readout (currently optional and displays only the time) 71 | - [ ] GPS 72 | - [ ] Top level sensor. E.g. a light sensor that cannot be obstructed 73 | 74 | 75 | #### 2. Determine your base 76 | 77 | **How many boxes did you check in step 1?** 78 | 79 | 1. No extra protoboards are needed (single-stack configuration) 80 | 2. You will need an Adafruit Feather Doubler proto board (https://www.adafruit.com/products/2890) 81 | 3. You will need an Adafruit Feather Tripler proto board (https://www.adafruit.com/products/3417) 82 | 83 | **Do you need WiFi?** 84 | 85 | (Note: WiFi development is currently on hold. Support TBD) 86 | 87 | **No:** The proto board selected above will be your base (or simply a single-stack configuration if no-extra proto). Thus your configuration is one of: 88 | 1. Single-stack 89 | 2. Double-stack (using a doubler proto to allow 2 top-layer components) 90 | 3. Triple-stack (using a tripler proto to allow 3 top-layer components) 91 | 92 | **Yes:** You may need to convert your configuration 93 | 1. Single-stack: Build the WiFi module on a doubler and convert to double-stack configuration 94 | 2. Double-stack: Build the WiFi module on a doubler. This is your base 95 | 3. Triple-stack: Build the WiFi module on a tripler. This is your base 96 | - No extra proto board (single-stack) use a double-stack WiFi and convert to 97 | 98 | #### 3. Choose your headers 99 | 100 | 1. Each top-layer component can use standard male header (https://www.adafruit.com/products/3002) 101 | 2. Middle-layer components require stacking headers (https://www.adafruit.com/products/2830) 102 | 3. Base layer can use regular Feather female headers (https://www.adafruit.com/products/2886) or short Feather headers (https://www.adafruit.com/products/2940) Note: for a WiFi module, you will probably want to use stacking headers. See WiFi tutorial for details 103 | 104 | **If in doubt, use stacking headers for everything -- this is the most versitile option for an uncertain future** 105 | 106 | 107 | ### Determine your parts 108 | 109 | - [x] Feather M0 RFM95 LoRa (https://www.adafruit.com/products/3178) 110 | - [x] Adalogger FeatherWing (https://www.adafruit.com/products/2922) 111 | - [x] Headers (see above) 112 | 113 | - [ ] OLED FeatherWing (https://www.adafruit.com/products/2900) 114 | - [ ] GPS FeatherWing (https://www.adafruit.com/products/3133) 115 | 116 | 117 | - [ ] WiFi (see separate WiFi tutorial, TBD) 118 | - [ ] Sensors (see separate sensor tutorials, TBD) 119 | 120 | ### Build the node modules 121 | 122 | * For the WiFi module, see the WiFi tutorial (TBD) 123 | * For sensor modules, see sensor tutorials (TBD) 124 | * For remaining modules, solder headers as planned above. The Adafruit tutorial for Feather assembly provides a good overview of assembly as appropriate for all Feathers and FeatherWings (https://learn.adafruit.com/adafruit-feather-m0-basic-proto/assembly). Be sure to solder the right headers onto the right component boards. If in doubt, use stacking headers 125 | -------------------------------------------------------------------------------- /legacy/RadioHead_1.79.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NUKnightLab/SensorGrid/33ce8d1358c6cdaa241d6065f5a627406d6265fe/legacy/RadioHead_1.79.tar.gz -------------------------------------------------------------------------------- /legacy/SHARP_GP2Y1010AU0F.cpp: -------------------------------------------------------------------------------- 1 | /* Adapted from: 2 | http://arduinodev.woofex.net/2012/12/01/standalone-sharp-dust-sensor/ 3 | & http://www.howmuchsnow.com/arduino/airquality/ 4 | */ 5 | #include "SHARP_GP2Y1010AU0F.h" 6 | 7 | namespace SHARP_GP2Y1010AU0F { 8 | 9 | static uint8_t _data_pin = 0; 10 | 11 | bool setup(uint8_t data_pin) 12 | { 13 | if (!data_pin) { 14 | Serial.println(F("Sharp GP2Y1010AU0F not configured")); 15 | return false; 16 | } 17 | _data_pin = data_pin; 18 | Serial.print(F("Setting Sharp GP2Y1010AU0F data pin to: ")); 19 | Serial.println(_data_pin, DEC); 20 | pinMode(DUST_SENSOR_LED_POWER, OUTPUT); 21 | pinMode(_data_pin, INPUT); 22 | return true; 23 | } 24 | 25 | /** 26 | * Legacy read function returns a float value 27 | */ 28 | float read_legacy() 29 | { 30 | if (_data_pin) { 31 | static float dust_sense_vo_measured = 0; 32 | static float dust_sense_calc_voltage = 0; 33 | static float dust_density = 0; 34 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_ON); 35 | delayMicroseconds(DUST_SENSOR_SAMPLING_TIME); 36 | dust_sense_vo_measured = analogRead(_data_pin); 37 | delayMicroseconds(DUST_SENSOR_DELTA_TIME); 38 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_OFF); 39 | delayMicroseconds(DUST_SENSOR_SLEEP_TIME); 40 | dust_sense_calc_voltage = dust_sense_vo_measured * (DUST_SENSOR_VCC / 1024); 41 | dust_density = 0.17 * dust_sense_calc_voltage - 0.1; 42 | Serial.print(F("Raw Signal Value (0-1023): ")); 43 | Serial.print(dust_sense_vo_measured, DEC); 44 | Serial.print(F(" - Voltage: ")); 45 | Serial.print(dust_sense_calc_voltage); 46 | Serial.print(F(" - Dust Density: ")); 47 | Serial.println(dust_density); 48 | return dust_density; 49 | } else { 50 | return 0.0; 51 | } 52 | } 53 | 54 | /** 55 | * Get the analogRead value from the dust data pin 56 | */ 57 | int32_t read() 58 | { 59 | if (_data_pin) { 60 | static float dust_sense_vo_measured = 0; 61 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_ON); 62 | delayMicroseconds(DUST_SENSOR_SAMPLING_TIME); 63 | dust_sense_vo_measured = analogRead(_data_pin); 64 | delayMicroseconds(DUST_SENSOR_DELTA_TIME); 65 | digitalWrite(DUST_SENSOR_LED_POWER, DUST_SENSOR_LED_OFF); 66 | delayMicroseconds(DUST_SENSOR_SLEEP_TIME); 67 | return dust_sense_vo_measured; 68 | } else { 69 | return 0; 70 | } 71 | } 72 | 73 | /** 74 | * Get the analogRead value from the dust data pin 75 | */ 76 | int32_t read_average(uint8_t num_samples) 77 | { 78 | if (_data_pin) { 79 | uint8_t max_samples = 100; 80 | if (num_samples > max_samples) { 81 | num_samples = max_samples; 82 | } 83 | int32_t total = 0; 84 | uint8_t count = 0; 85 | for (int i=0; i 0) { 88 | total += val; 89 | count++; 90 | } 91 | } 92 | if (count == 0) return 0; 93 | return total / count; 94 | } else { 95 | return 0; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /legacy/SHARP_GP2Y1010AU0F.h: -------------------------------------------------------------------------------- 1 | #ifndef SHARP_GP2Y1010AU0F_H 2 | #define SHARP_GP2Y1010AU0F_H 3 | 4 | #include 5 | 6 | #define DUST_SENSOR_LED_POWER 12 7 | #define DUST_SENSOR_SAMPLING_TIME 280 8 | #define DUST_SENSOR_DELTA_TIME 40 9 | #define DUST_SENSOR_SLEEP_TIME 9680 10 | #define DUST_SENSOR_VCC 3.3 11 | #define DUST_SENSOR_LED_ON LOW 12 | #define DUST_SENSOR_LED_OFF HIGH 13 | 14 | namespace SHARP_GP2Y1010AU0F { 15 | void setDataPin(uint8_t pin); 16 | bool setup(uint8_t data_pin); 17 | float read_legacy(); 18 | int32_t read(); 19 | int32_t read_average(uint8_t num_samples); 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /legacy/SensorGrid.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSORGRID_H 2 | #define SENSORGRID_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "io.h" 12 | #include 13 | 14 | #define CAD_TIMEOUT 1000 15 | #define TIMEOUT 1000 16 | #define REQUIRED_RH_VERSION_MAJOR 1 17 | #define REQUIRED_RH_VERSION_MINOR 82 18 | #define MAX_MESSAGE_SIZE 255 19 | 20 | /** 21 | * Overall max message size is somewhere between 244 and 247 bytes. 247 will cause invalid length error 22 | * 23 | * Note that these max sizes on the message structs are system specific due to struct padding. The values 24 | * here are specific to the Cortex M0 25 | * 26 | */ 27 | #define MAX_DATA_RECORDS 39 28 | #define MAX_NODES 230 29 | 30 | 31 | 32 | /* * 33 | * Message types: 34 | * Not using enum for message types to ensure small numeric size 35 | */ 36 | #define MESSAGE_TYPE_NO_MESSAGE 0 37 | #define MESSAGE_TYPE_CONTROL 1 38 | #define MESSAGE_TYPE_DATA 2 39 | #define MESSAGE_TYPE_UNKNOWN -1 40 | #define MESSAGE_TYPE_MESSAGE_ERROR -2 41 | #define MESSAGE_TYPE_NONE_BUFFER_LOCK -3 42 | #define MESSAGE_TYPE_WRONG_VERSION -4 43 | #define MESSAGE_TYPE_WRONG_NETWORK -5 // for testing only. Normally we will just skip messages from other networks 44 | 45 | /** 46 | * Control codes 47 | */ 48 | //#define CONTROL_SEND_DATA 1 49 | #define CONTROL_NONE 1 // no-op used for testing 50 | #define CONTROL_NEXT_ACTIVITY_TIME 2 51 | #define CONTROL_ADD_NODE 3 52 | 53 | /* Data types */ 54 | #define DATA_TYPE_BATTERY_LEVEL 1 55 | #define DATA_TYPE_SHARP_GP2Y1010AU0F 2 56 | 57 | /* Module defs */ 58 | #include "WINC1500.h" 59 | 60 | #define CONFIG_FILE "CONFIG.TXT" // Adalogger doesn't seem to like underscores in the name!!! 61 | #define MAX_NETWORK_SIZE 100 62 | #define LED 13 63 | #define VBATPIN 9 64 | #define BUTTON_A 9 65 | #define BUTTON_B 6 66 | #define BUTTON_C 5 67 | 68 | #define CONTROL_TYPE_ACK 1 69 | #define CONTROL_TYPE_SEND_DATA 2 70 | #define CONTROL_TYPE_SLEEP 3 71 | 72 | extern bool oled_is_on; 73 | extern bool WiFiPresent; 74 | 75 | extern RTC_PCF8523 rtc; 76 | extern bool sensorSi1145Module; 77 | 78 | enum ERRORS { 79 | NO_ERR, 80 | MESSAGE_STRUCT_TOO_LARGE, 81 | LORA_INIT_FAIL, 82 | LORA_FREQ_FAIL, 83 | WIFI_MODULE_NOT_DETECTED, 84 | FAILED_CONFIG_FILE_READ 85 | }; 86 | 87 | #if defined(ARDUINO_ARCH_SAMD) 88 | 89 | extern Adafruit_FeatherOLED display; 90 | extern uint32_t display_clock_time; 91 | 92 | typedef struct Control { 93 | uint8_t id; 94 | uint8_t code; 95 | uint8_t from_node; 96 | int16_t data; 97 | uint8_t nodes[MAX_NODES]; 98 | }; 99 | 100 | typedef struct Data { 101 | uint8_t id; // 1-255 indicates Data 102 | uint8_t node_id; 103 | uint8_t timestamp; 104 | int8_t type; 105 | int16_t value; 106 | }; 107 | 108 | typedef struct Message { 109 | uint8_t sensorgrid_version; 110 | uint8_t network_id; 111 | uint8_t from_node; 112 | uint8_t message_type; 113 | uint8_t len; 114 | union { 115 | struct Control control; 116 | struct Data data[MAX_DATA_RECORDS]; 117 | }; 118 | }; 119 | 120 | #else 121 | #error Unsupported architecture 122 | #endif 123 | 124 | /* 125 | * TODO on M0: 126 | * From: https://learn.adafruit.com/adafruit-feather-m0-radio-with-lora-radio-module?view=all 127 | * If you're used to AVR, you've probably used PROGMEM to let the compiler know you'd like to put a variable or string in flash memory to save on RAM. On the ARM, its a little easier, simply add const before the variable name: 128 | const char str[] = "My very long string"; 129 | That string is now in FLASH. You can manipulate the string just like RAM data, the compiler will automatically read from FLASH so you dont need special progmem-knowledgeable functions. 130 | You can verify where data is stored by printing out the address: 131 | Serial.print("Address of str $"); Serial.println((int)&str, HEX); 132 | If the address is $2000000 or larger, its in SRAM. If the address is between $0000 and $3FFFF Then it is in FLASH 133 | */ 134 | 135 | class __FlashStringHelper; 136 | #define F(string_literal) (reinterpret_cast(PSTR(string_literal))) 137 | 138 | void fail(enum ERRORS err); 139 | void flashLED(int times, int endState); 140 | float batteryLevel(); 141 | void printRam(); 142 | int freeRam(); 143 | void aButton_ISR(); 144 | void p(char *fmt, ...); 145 | void p(const __FlashStringHelper *fmt, ... ); 146 | 147 | #endif 148 | -------------------------------------------------------------------------------- /legacy/WINC1500.cpp: -------------------------------------------------------------------------------- 1 | #include "WINC1500.h" 2 | 3 | static bool WiFiPresent = false; 4 | 5 | WiFiClient WIFI_CLIENT; 6 | 7 | void printWiFiStatus() { 8 | // print the SSID of the network you're attached to: 9 | Serial.print("SSID: "); 10 | Serial.println(WiFi.SSID()); 11 | 12 | // print your WiFi shield's IP address: 13 | IPAddress ip = WiFi.localIP(); 14 | Serial.print("IP Address: "); 15 | Serial.println(ip); 16 | 17 | // print the received signal strength: 18 | long rssi = WiFi.RSSI(); 19 | Serial.print("signal strength (RSSI):"); 20 | Serial.print(rssi); 21 | Serial.println(" dBm"); 22 | } 23 | 24 | static bool connectWiFi(const char* wifi_ssid, const char* wifi_pass) 25 | { 26 | //WiFi.end(); // This is here to make the keep-alive work, but not sure 27 | // why we are even having to reconnect for every request 28 | Serial.println("Checking connection ..."); 29 | if (WiFi.status() != WL_CONNECTED) { 30 | Serial.print("WiFi Status: "); Serial.println(WiFi.status()); 31 | Serial.print(F("CON SSID: ")); 32 | Serial.println(wifi_ssid); 33 | Serial.print(F("CON PASS: ")); Serial.println(wifi_pass); 34 | // Connect to WPA/WPA2 network. Change this line if using open or WEP network: 35 | //WIFI_STATUS = WiFi.begin(WIFI_SSID, WIFI_PASS); 36 | Serial.println("Connecting .."); 37 | WiFi.begin(wifi_ssid, wifi_pass); 38 | uint8_t timeout = 10; 39 | while (timeout && (WiFi.status() != WL_CONNECTED)) { 40 | timeout--; 41 | delay(1000); 42 | } 43 | } 44 | if (WiFi.status() != WL_CONNECTED) { 45 | Serial.println("WARNING: Could not connect to WiFi"); 46 | return false; 47 | } 48 | Serial.println(F("WIFI:\n ")); 49 | printWifiStatus(); 50 | return true; 51 | } 52 | 53 | void connectToServer(WiFiClient& client,char ssid[],char pass[], char host[], int port) { 54 | int status = WL_IDLE_STATUS; 55 | //client; 56 | WiFi.setPins(WIFI_CS, WIFI_IRQ, WIFI_RST, WIFI_EN); 57 | if (WiFi.status() == WL_NO_SHIELD) { 58 | Serial.println("WiFi shield not present"); 59 | //don't continue 60 | while (true); 61 | } 62 | 63 | while (status!= WL_CONNECTED) { 64 | Serial.print("Attempting to connect to SSID: "); 65 | Serial.println(ssid); 66 | Serial.print("Using password: "); 67 | Serial.println(pass); 68 | status = WiFi.begin(ssid, pass); 69 | delay(10000); //wait 10 seconds for connection 70 | Serial.println("Connected to WiFi"); 71 | printWiFiStatus(); 72 | Serial.println("\nStarting connection to server..."); 73 | if (client.connect(host, port)) { 74 | Serial.println("connected to server"); 75 | } 76 | else { 77 | Serial.println("server conncetion failed"); 78 | } 79 | } 80 | } 81 | 82 | extern "C" char *sbrk(int i); 83 | static int _freeRam() 84 | { 85 | char stack_dummy = 0; 86 | return &stack_dummy - sbrk(0); 87 | } 88 | 89 | static bool _postToAPI(const char* apiServer, const char* apiHost, const int apiPort, char* msg, uint8_t msgLen) 90 | { 91 | if (WIFI_CLIENT.connect(apiServer, apiPort)) { 92 | Serial.println(F("API:")); 93 | Serial.print(F(" CON: ")); 94 | Serial.print(apiServer); 95 | Serial.print(F(":")); Serial.println(apiPort); 96 | //char* messageChars = (char*)msgBytes; 97 | //char* messageChars = (char*)*msg; 98 | WIFI_CLIENT.println("POST /data HTTP/1.1"); 99 | WIFI_CLIENT.print("Host: "); WIFI_CLIENT.println(apiHost); 100 | WIFI_CLIENT.println("User-Agent: ArduinoWiFi/1.1"); 101 | WIFI_CLIENT.println("Connection: close"); 102 | WIFI_CLIENT.println("Content-Type: application/x-www-form-urlencoded"); 103 | WIFI_CLIENT.print("Content-Length: "); WIFI_CLIENT.println(msgLen); 104 | Serial.print(F("Message length is: ")); Serial.println(msgLen); 105 | WIFI_CLIENT.println(); 106 | WIFI_CLIENT.write(msg, msgLen); 107 | WIFI_CLIENT.println(); 108 | Serial.println(F("Completed post to API")); 109 | Serial.println(_freeRam()); 110 | return true; 111 | } else { 112 | return false; 113 | } 114 | } 115 | 116 | //bool postToAPI(const char* wifi_ssid, const char* wifi_pass, const char* apiServer, const char* apiHost, const int apiPort, uint8_t msgBytes[], uint8_t msgLen) { 117 | bool postToAPI(const char* wifi_ssid, const char* wifi_pass, const char* apiServer, const char* apiHost, const int apiPort, char* msg, uint8_t msgLen) 118 | { 119 | if (!WiFiPresent || !connectWiFi(wifi_ssid, wifi_pass)) { 120 | return false; 121 | } 122 | if (_postToAPI(apiServer, apiHost, apiPort, msg, msgLen)) { 123 | return true; 124 | } else { 125 | Serial.println(F("FAIL: API CON. RETRY ..")); 126 | WiFi.end(); 127 | if (connectWiFi(wifi_ssid, wifi_pass)) { 128 | _postToAPI(apiServer, apiHost, apiPort, msg, msgLen); 129 | } 130 | } 131 | return false; 132 | } 133 | 134 | bool setupWiFi(const char* wifi_ssid, const char* wifi_pass) 135 | { 136 | Serial.print(F("WiFi Module.. ")); 137 | #if defined(ARDUINO_ARCH_SAMD) 138 | /** 139 | * void setPins(int8_t cs, int8_t irq, int8_t rst, int8_t en = -1) 140 | * 141 | * cs: pull down during rx/tx 142 | * 143 | */ 144 | WiFi.setPins(6,11,12); // Adafruit uses 8,7,4 in tutorials, but these are used by LoRa 145 | // Adalogger uses 10 146 | if (WiFi.status() == WL_NO_SHIELD) { 147 | Serial.println(F(" ..Not detected")); 148 | } else { 149 | WiFiPresent = true; 150 | Serial.println(F(" ..Detected")); 151 | Serial.println(F("Connecting to WiFi..")); 152 | connectWiFi(wifi_ssid, wifi_pass); 153 | } 154 | #else 155 | #error Unsupported architecture 156 | #endif 157 | return WiFiPresent; 158 | } 159 | -------------------------------------------------------------------------------- /legacy/WINC1500.h: -------------------------------------------------------------------------------- 1 | #ifndef WINC1500_H 2 | #define WINC1500_H 3 | #include 4 | 5 | 6 | #if defined(__AVR_ATmega32U4__) 7 | #include 8 | #include 9 | #define Serial SerialUSB 10 | #elif defined(ARDUINO_ARCH_SAMD) 11 | #include 12 | extern WiFiClient WIFI_CLIENT; 13 | #else 14 | #error Unsupported architecture 15 | #endif 16 | 17 | #define WIFI_CS 8 18 | #define WIFI_IRQ 7 19 | #define WIFI_RST 4 20 | #define WIFI_EN 2 21 | 22 | void printWifiStatus(); 23 | void connectToServer(WiFiClient& client,char ssid[],char pass[], char host[], int port); 24 | bool postToAPI(const char* wifi_ssid, const char* wifi_pass, const char* apiServer, const char* apiHost, const int apiPort, char* msg, uint8_t msgLen); 25 | bool setupWiFi(const char* wifi_ssid, const char* wifi_pass); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /legacy/api.py: -------------------------------------------------------------------------------- 1 | import flask 2 | from flask import Flask, request 3 | app = Flask(__name__) 4 | import datetime 5 | import pymongo 6 | import os 7 | import sys 8 | from delorean import parse 9 | from struct import * 10 | 11 | MONGO_DB_URI = os.environ.get('MONGO_DB_URI', 'mongodb://localhost:27017/') 12 | db = pymongo.MongoClient(MONGO_DB_URI).sensorgrid 13 | 14 | PROTOCOL_VERSIONS = { 15 | '0.11': ' 5 | #include 6 | #include 7 | #include "display.h" 8 | #include "config.h" 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #define RH_MESH_MAX_MESSAGE_LEN 60 15 | 16 | void setupRadio(RH_RF95 rf95); 17 | void reconnectClient(WiFiClient& client, char* ssid); 18 | void postToAPI(WiFiClient& client,int fromNode, int ID); 19 | bool sendCurrentMessage(RH_RF95 rf95, int dest); 20 | void writeLogLine(int fromNode, int id); 21 | void receive(); 22 | void waitForInstructions(RH_RF95 rf95); 23 | void collectFromNode(int toID, uint32_t nextCollectTime, WiFiClient& client, char* ssid); 24 | void writeToSD(char* filename, char* str); 25 | void fillCurrentMessageData(); 26 | void printMessageData(int fromNode); 27 | 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /legacy/lib/dtostrf.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * From: http://forum.arduino.cc/index.php?topic=368720.0 3 | * 4 | * 5 | * dtostrf - Emulation for dtostrf function from avr-libc 6 | * Copyright (c) 2015 Arduino LLC. All rights reserved. 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Lesser General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2.1 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Lesser General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Lesser General Public 19 | * License along with this library; if not, write to the Free Software 20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 | * 22 | *****************************************************************************/ 23 | #include 24 | 25 | #if 0 26 | char *dtostrf (double val, signed char width, unsigned char prec, char *sout) { 27 | char fmt[20]; 28 | sprintf(fmt, "%%%d.%df", width, prec); 29 | sprintf(sout, fmt, val); 30 | return sout; 31 | } 32 | #else 33 | #include 34 | #include 35 | char *dtostrf(double val, int width, unsigned int prec, char *sout) 36 | { 37 | int decpt, sign, reqd, pad; 38 | const char *s, *e; 39 | char *p; 40 | s = fcvt(val, prec, &decpt, &sign); 41 | if (prec == 0 && decpt == 0) { 42 | s = (*s < '5') ? "0" : "1"; 43 | reqd = 1; 44 | } else { 45 | reqd = strlen(s); 46 | if (reqd > decpt) reqd++; 47 | if (decpt == 0) reqd++; 48 | } 49 | if (sign) reqd++; 50 | p = sout; 51 | e = p + reqd; 52 | pad = width - reqd; 53 | if (pad > 0) { 54 | e += pad; 55 | while (pad-- > 0) *p++ = ' '; 56 | } 57 | if (sign) *p++ = '-'; 58 | if (decpt <= 0 && prec > 0) { 59 | *p++ = '0'; 60 | *p++ = '.'; 61 | e++; 62 | while ( decpt < 0 ) { 63 | decpt++; 64 | *p++ = '0'; 65 | } 66 | } 67 | while (p < e) { 68 | *p++ = *s++; 69 | if (p == e) break; 70 | if (--decpt == 0) *p++ = '.'; 71 | } 72 | if (width < 0) { 73 | pad = (reqd + width) * -1; 74 | while (pad-- > 0) *p++ = ' '; 75 | } 76 | *p = 0; 77 | return sout; 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /legacy/requirements.txt: -------------------------------------------------------------------------------- 1 | Babel==2.5.1 2 | click==6.7 3 | Delorean==0.6.0 4 | Flask==0.12.1 5 | humanize==0.5.1 6 | itsdangerous==0.24 7 | Jinja2==2.9.6 8 | MarkupSafe==1.0 9 | pymongo==3.4.0 10 | pyserial==3.3 11 | python-dateutil==2.6.1 12 | pytz==2017.2 13 | requests==2.13.0 14 | six==1.11.0 15 | tzlocal==1.4 16 | Werkzeug==0.12.1 17 | -------------------------------------------------------------------------------- /legacy/timer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Timer interrupt code adapted from gist: 3 | * https://gist.github.com/jdneo/43be30d85080b175cb5aed3500d3f989 4 | * 5 | * Not currently being used but kept here for reference. Note: Use of TC3 6 | * conflicts with RadioHead 7 | 8 | * Call with , e.g. startTimer(10); 9 | */ 10 | #include "timer.h" 11 | 12 | #define LED_PIN 13 13 | 14 | #define CPU_HZ 48000000 15 | #define TIMER_PRESCALER_DIV 1024 16 | 17 | void startTimer(int frequencyHz); 18 | void setTimerFrequency(int frequencyHz); 19 | void TC3_Handler(); 20 | 21 | bool isLEDOn = false; 22 | 23 | void setTimerFrequency(int frequencyHz) { 24 | int compareValue = (CPU_HZ / (TIMER_PRESCALER_DIV * frequencyHz)) - 1; 25 | TcCount16* TC = (TcCount16*) TC3; 26 | // Make sure the count is in a proportional position to where it was 27 | // to prevent any jitter or disconnect when changing the compare value. 28 | TC->COUNT.reg = map(TC->COUNT.reg, 0, TC->CC[0].reg, 0, compareValue); 29 | TC->CC[0].reg = compareValue; 30 | Serial.println(TC->COUNT.reg); 31 | Serial.println(TC->CC[0].reg); 32 | while (TC->STATUS.bit.SYNCBUSY == 1); 33 | } 34 | 35 | void startTimer(int frequencyHz) { 36 | REG_GCLK_CLKCTRL = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID_TCC2_TC3) ; 37 | while ( GCLK->STATUS.bit.SYNCBUSY == 1 ); // wait for sync 38 | 39 | TcCount16* TC = (TcCount16*) TC3; 40 | 41 | TC->CTRLA.reg &= ~TC_CTRLA_ENABLE; 42 | while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 43 | 44 | // Use the 16-bit timer 45 | TC->CTRLA.reg |= TC_CTRLA_MODE_COUNT16; 46 | while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 47 | 48 | // Use match mode so that the timer counter resets when the count matches the compare register 49 | TC->CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; 50 | while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 51 | 52 | // Set prescaler to 1024 53 | TC->CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1024; 54 | while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 55 | 56 | setTimerFrequency(frequencyHz); 57 | 58 | // Enable the compare interrupt 59 | TC->INTENSET.reg = 0; 60 | TC->INTENSET.bit.MC0 = 1; 61 | 62 | NVIC_EnableIRQ(TC3_IRQn); 63 | 64 | TC->CTRLA.reg |= TC_CTRLA_ENABLE; 65 | while (TC->STATUS.bit.SYNCBUSY == 1); // wait for sync 66 | } 67 | 68 | void TC3_Handler() { 69 | TcCount16* TC = (TcCount16*) TC3; 70 | // If this interrupt is due to the compare register matching the timer count 71 | // we toggle the LED. 72 | if (TC->INTFLAG.bit.MC0 == 1) { 73 | TC->INTFLAG.bit.MC0 = 1; 74 | // Write callback here!!! 75 | digitalWrite(LED_PIN, isLEDOn); 76 | isLEDOn = !isLEDOn; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /legacy/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef __TIMER__ 2 | #define __TIMER__ 3 | 4 | #include 5 | 6 | extern void startTimer(int frequencyHz); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /legacy/util.cpp: -------------------------------------------------------------------------------- 1 | #include "SensorGrid.h" 2 | 3 | #if defined(__AVR_ATmega32U4__) 4 | /* Here for reference. 32u4 no longer supported */ 5 | static int _freeRam () { 6 | extern int __heap_start, *__brkval; 7 | int v; 8 | return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 9 | } 10 | #elif defined(ARDUINO_ARCH_SAMD) 11 | extern "C" char *sbrk(int i); 12 | static int _freeRam() 13 | { 14 | char stack_dummy = 0; 15 | return &stack_dummy - sbrk(0); 16 | } 17 | #endif 18 | 19 | 20 | #include 21 | void p(char *fmt, ... ){ 22 | char buf[128]; // resulting string limited to 128 chars 23 | va_list args; 24 | va_start (args, fmt ); 25 | vsnprintf(buf, 128, fmt, args); 26 | va_end (args); 27 | Serial.print(buf); 28 | } 29 | 30 | void p(const __FlashStringHelper *fmt, ... ){ 31 | char buf[128]; // resulting string limited to 128 chars 32 | va_list args; 33 | va_start (args, fmt); 34 | #ifdef __AVR__ 35 | vsnprintf_P(buf, sizeof(buf), (const char *)fmt, args); // progmem for AVR 36 | #else 37 | vsnprintf(buf, sizeof(buf), (const char *)fmt, args); // for the rest of the world 38 | #endif 39 | va_end(args); 40 | Serial.print(buf); 41 | } 42 | 43 | void fail(enum ERRORS err) 44 | { 45 | Serial.print(F("ERR: ")); 46 | Serial.println(err); 47 | while(1); 48 | } 49 | 50 | void flashLED(int times, int endState) 51 | { 52 | digitalWrite(LED, LOW); delay(50); 53 | for (int i=0; i 4 | maintainer=Scott B. Bradley <@scott2b> 5 | sentence=Miscellaneous utilities for Knight Lab Arduino projects 6 | paragraph=A collection of some general utilities we use in embedded projects at the Knight Lab 7 | category=Other 8 | url= 9 | architectures=avr,samd 10 | includes=KnightLab_ArduinoUtils.h 11 | -------------------------------------------------------------------------------- /lib/KnightLab_ArduinoUtils/readme.txt: -------------------------------------------------------------------------------- 1 | Could not use Adafruit_SleepDog library because there was a conflict with the TC3_Handler() from ASFCore. Currently using WatchDogSAMD.cpp /.h under arch with 2 | ASFCore commented to handle the conflict 3 | -------------------------------------------------------------------------------- /lib/KnightLab_ArduinoUtils/src/KnightLab_ArduinoUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "KnightLab_ArduinoUtils.h" 2 | 3 | void fail(int code) 4 | { 5 | while(1) { 6 | for (int i=0; i 5 | #include 6 | 7 | #ifdef ARDUINO_ARCH_SAMD 8 | #include 9 | #endif 10 | 11 | #define WAIT_SERIAL_TIMEOUT 10000 12 | #define LOGGING_BUFFER_SIZE 128 13 | 14 | extern void set_logging(bool b); 15 | extern bool is_logging(); 16 | extern void fail(int code); 17 | extern void ftoa(float n, char *res, int afterpoint); 18 | 19 | #if defined(ARDUINO_ARCH_SAMD) 20 | // Arduino Zero / ATSAMD series CPU watchdog support. 21 | #include "arch/WatchdogSAMD.h" 22 | typedef WatchdogSAMD WatchdogType; 23 | 24 | #else 25 | serial.println("Arch type other than SAMD") 26 | while (1) {} 27 | #endif 28 | 29 | class __FlashStringHelper; 30 | //#define F(string_literal) (reinterpret_cast(PSTR(string_literal))) 31 | 32 | /** 33 | * Some really basic utilities for printing / logging (with timestamps) to serial 34 | * 35 | * For M0, internal RTC is used for timestamps. The RTC should be initialized 36 | * and set before calling any of the logging functions 37 | * 38 | * Note: log_ has a dumb underscore to avoid conflicting with the mathematical 39 | * log function. I am open to better ideas for the name of this function. -SBB 40 | */ 41 | extern void print(char *fmt, ... ); 42 | extern void print(const __FlashStringHelper *fmt, ... ); 43 | extern void println(char *fmt, ... ); 44 | extern void println(const __FlashStringHelper *fmt, ... ); 45 | extern void log_(char *fmt, ... ); 46 | extern void log_(const __FlashStringHelper *fmt, ... ); 47 | extern void logln(char *fmt, ... ); 48 | extern void logln(const __FlashStringHelper *fmt, ... ); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /lib/KnightLab_ArduinoUtils/src/arch/WatchdogSAMD.h: -------------------------------------------------------------------------------- 1 | #ifndef SENSORGRIDPM_WATCHDOGSAMD_H_ 2 | #define SENSORGRIDPM_WATCHDOGSAMD_H_ 3 | 4 | class WatchdogSAMD { 5 | public: 6 | WatchdogSAMD(): 7 | _initialized(false) 8 | {} 9 | 10 | // Enable the watchdog timer to reset the machine after a period of time 11 | // without any calls to reset(). The passed in period (in milliseconds) 12 | // is just a suggestion and a lower value might be picked if the hardware 13 | // does not support the exact desired value. 14 | // User code should NOT set the 'isForSleep' argument either way -- 15 | // it's used internally by the library, but your sketch should leave this 16 | // out when calling enable(), just let the default have its way. 17 | // 18 | // The actual period (in milliseconds) before a watchdog timer reset is 19 | // returned. 20 | int enable(int maxPeriodMS = 0, bool isForSleep = false); 21 | 22 | // Reset or 'kick' the watchdog timer to prevent a reset of the device. 23 | void reset(); 24 | 25 | // Completely disable the watchdog timer. 26 | void disable(); 27 | 28 | // Enter the lowest power sleep mode (using the watchdog timer) for the 29 | // desired period of time. The passed in period (in milliseconds) is 30 | // just a suggestion and a lower value might be picked if the hardware 31 | // does not support the exact desired value 32 | // 33 | // The actual period (in milliseconds) that the hardware was asleep will be 34 | // returned. 35 | int sleep(int maxPeriodMS = 0); 36 | 37 | private: 38 | void _initialize_wdt(); 39 | 40 | bool _initialized; 41 | }; 42 | 43 | #endif // SENSORGRIDPM_WATCHDOGSAMD_H_ 44 | -------------------------------------------------------------------------------- /lib/KnightLab_FeatherUtils/library.properties: -------------------------------------------------------------------------------- 1 | name=KnightLab_FeatherUtils 2 | version=1.0 3 | author=Scott B. Bradley <@scott2b> 4 | maintainer=Scott B. Bradley <@scott2b> 5 | sentence=Miscellaneous utilities for the Adafruit Feather prototyping system 6 | paragraph=Miscellaneous utilities for the Adafruit Feather prototyping system 7 | category=Other 8 | url= 9 | architectures=avr,samd 10 | includes=KnightLab_FeatherUtils.h 11 | -------------------------------------------------------------------------------- /lib/KnightLab_FeatherUtils/src/KnightLab_FeatherUtils.cpp: -------------------------------------------------------------------------------- 1 | #include "KnightLab_FeatherUtils.h" 2 | 3 | 4 | float batteryLevel() 5 | { 6 | float val = analogRead(VBATPIN); 7 | val *= 2; // we divided by 2, so multiply back 8 | val *= 3.3; // Multiply by 3.3V, our reference voltage 9 | val /= 1024; // convert to voltage 10 | return val; 11 | } 12 | 13 | /** 14 | * Take the average of 3 samples, not including outliers 15 | */ 16 | float batteryLevelAveraged() 17 | { 18 | float bat_1, bat_2, bat_3; 19 | bat_1 = batteryLevel(); 20 | delay(100); 21 | bat_2 = batteryLevel(); 22 | delay(100); 23 | bat_3 = batteryLevel(); 24 | float bat_mean = (bat_1 + bat_2 + bat_3) / 3; 25 | float bat_std = sqrt( 26 | (pow((bat_1 - bat_mean), 2) + 27 | pow((bat_2 - bat_mean), 2) + 28 | pow((bat_3 - bat_mean), 2)) / 3); 29 | float total = 0.0; 30 | int count = 0; 31 | float std_cutoff = 1.0; // samples within 1 standard deviation expected 32 | if (abs(bat_1 - bat_mean) <= std_cutoff * bat_std) { 33 | total += bat_1; 34 | count++; 35 | } 36 | if (abs(bat_2 - bat_mean) <= std_cutoff * bat_std) { 37 | total += bat_2; 38 | count++; 39 | } 40 | if (abs(bat_3 - bat_mean) <= std_cutoff * bat_std) { 41 | total += bat_3; 42 | count++; 43 | } 44 | if (count == 0) { // in case nothing passes the standard deviation test 45 | return (bat_1 + bat_2 + bat_3) / 3; 46 | } 47 | return total / count; 48 | } -------------------------------------------------------------------------------- /lib/KnightLab_FeatherUtils/src/KnightLab_FeatherUtils.h: -------------------------------------------------------------------------------- 1 | #ifndef __KL_FEATHER_UTILS__ 2 | #define __KL_FEATHER_UTILS__ 3 | 4 | #include 5 | 6 | #define VBATPIN 9 7 | 8 | extern float batteryLevel(); 9 | extern float batteryLevelAveraged(); 10 | 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /lib/KnightLab_Sensors/library.properties: -------------------------------------------------------------------------------- 1 | name=KnightLab_Sensors 2 | version=1.0 3 | author=Scott B. Bradley <@scott2b> 4 | maintainer=Scott B. Bradley <@scott2b> 5 | sentence=Sensor integration code for the SensorGrid project 6 | paragraph=Sensor integration code for the SensorGrid project 7 | category=Other 8 | url= 9 | architectures=avr,samd 10 | includes=HONEYWELL_HPM.h 11 | -------------------------------------------------------------------------------- /lib/KnightLab_Sensors/src/HONEYWELL_HPM.h: -------------------------------------------------------------------------------- 1 | #ifndef __SENSORGRID_HONEYWELL_HPM__ 2 | #define __SENSORGRID_HONEYWELL_HPM__ 3 | 4 | #include "KnightLab_SensorInterface.h" 5 | 6 | // don't set this to < 2000 w/o testing valid results from sensor 7 | #define UART_TIMEOUT 2000 8 | 9 | class HONEYWELL_HPM : public SensorInterface { 10 | TimeFunction _time_fcn; 11 | uint8_t _node_id; 12 | 13 | public: 14 | HONEYWELL_HPM(uint8_t node_id, TimeFunction time_fcn); 15 | virtual ~HONEYWELL_HPM(); 16 | bool setup(); 17 | bool start(); 18 | size_t read(char* buf, int len); 19 | //size_t read(char buf[], int len); 20 | bool stop(); 21 | 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /lib/KnightLab_Sensors/src/KL_ADAFRUIT_SI7021.cpp: -------------------------------------------------------------------------------- 1 | #include "KL_ADAFRUIT_SI7021.h" 2 | 3 | /* Adafruit Si7021 temperature/humidity breakout */ 4 | 5 | static Adafruit_Si7021 sensor = Adafruit_Si7021(); 6 | 7 | float ADAFRUIT_SI7021::readTemperature() { 8 | return sensor.readTemperature() * 1.8 + 32; 9 | } 10 | 11 | float ADAFRUIT_SI7021::readHumidity() { 12 | return sensor.readHumidity(); 13 | } 14 | 15 | ADAFRUIT_SI7021::ADAFRUIT_SI7021(uint8_t node_id, TimeFunction time_fcn) { 16 | id = "SI7021_TEMP_HUMIDITY"; 17 | _node_id = node_id; 18 | _time_fcn = time_fcn; 19 | } 20 | 21 | ADAFRUIT_SI7021::~ADAFRUIT_SI7021() {} 22 | 23 | bool ADAFRUIT_SI7021::setup() { 24 | Serial.print(F("Si7021 ")); 25 | if (sensor.begin()) { 26 | Serial.println(F("Found Si7021 with serial number: ")); 27 | Serial.print((byte)(sensor.sernum_a >> 24), HEX); 28 | Serial.print(" "); 29 | Serial.print((byte)(sensor.sernum_a >> 16), HEX); 30 | Serial.print(" "); 31 | Serial.print((byte)(sensor.sernum_a >> 8), HEX); 32 | Serial.print(" "); 33 | Serial.print((byte)sensor.sernum_a, HEX); 34 | Serial.print(" :: "); 35 | Serial.println((byte)(sensor.sernum_b >> 24), HEX); 36 | Serial.print(" "); 37 | Serial.print((byte)(sensor.sernum_b >> 16), HEX); 38 | Serial.print(" "); 39 | Serial.print((byte)(sensor.sernum_b >> 8), HEX); 40 | Serial.print(" "); 41 | Serial.print((byte)sensor.sernum_b, HEX); 42 | return true; 43 | } else { 44 | Serial.println(F("Not Found")); 45 | return false; 46 | } 47 | } 48 | 49 | bool ADAFRUIT_SI7021::start() { 50 | return true; 51 | } 52 | 53 | size_t ADAFRUIT_SI7021::read(char buf[], int len) { 54 | char temp[7]; 55 | char humid[7]; 56 | ftoa(readTemperature(), temp, 2); 57 | ftoa(readHumidity(), humid, 2); 58 | snprintf(buf, len, 59 | "{\"node\":%d,\"tmp\":%s,\"hmd\":%s,\"ts\":%lu}", 60 | this->_node_id, temp, humid, this->_time_fcn()); 61 | Serial.println(buf); 62 | return strlen(buf); 63 | } 64 | 65 | bool ADAFRUIT_SI7021::stop() { 66 | return true; 67 | } 68 | -------------------------------------------------------------------------------- /lib/KnightLab_Sensors/src/KL_ADAFRUIT_SI7021.h: -------------------------------------------------------------------------------- 1 | #ifndef __KNIGHTLAB__ADAFRUIT_SI7021_H 2 | #define __KNIGHTLAB__ADAFRUIT_SI7021_H 3 | 4 | #include 5 | #include "KnightLab_SensorInterface.h" 6 | 7 | class ADAFRUIT_SI7021 : public SensorInterface { 8 | TimeFunction _time_fcn; 9 | uint8_t _node_id; 10 | 11 | public: 12 | ADAFRUIT_SI7021(uint8_t node_id, TimeFunction time_fcn); 13 | virtual ~ADAFRUIT_SI7021(); 14 | bool setup(); 15 | bool start(); 16 | size_t read(char* buf, int len); 17 | bool stop(); 18 | float readTemperature(); 19 | float readHumidity(); 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /lib/KnightLab_Sensors/src/KnightLab_SensorConfig.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2018 Northwestern University 3 | */ 4 | #include "KnightLab_SensorConfig.h" 5 | 6 | struct SensorConfig *sensor_config_head; 7 | 8 | static SensorConfig *getNextSensorConfig(SensorConfig *current_config) { 9 | SensorConfig *new_config; 10 | if (current_config == NULL) { 11 | new_config = new SensorConfig(); 12 | sensor_config_head = new_config; 13 | } else { 14 | current_config->next = new SensorConfig(); 15 | new_config = current_config->next; 16 | } 17 | return new_config; 18 | } 19 | 20 | void loadSensorConfig(uint8_t node_id, TimeFunction time_fcn) { 21 | // struct SensorConfig *current_config = new SensorConfig(); 22 | SensorConfig *current_config = NULL; 23 | SensorInterface *sensor; 24 | 25 | /* Adafruit Si7021 temperature/humidity breakout */ 26 | sensor = new ADAFRUIT_SI7021(node_id, time_fcn); 27 | if (sensor->setup()) { 28 | Serial.println("Configuring ADAFRUIT_SI7021"); 29 | current_config = getNextSensorConfig(current_config); 30 | current_config->sensor = sensor; 31 | } else { 32 | delete sensor; 33 | } 34 | 35 | sensor = new HONEYWELL_HPM(node_id, time_fcn); 36 | if (sensor->setup()) { 37 | Serial.println("Configuring HONEYWELL_HPM"); 38 | current_config = getNextSensorConfig(current_config); 39 | current_config->sensor = sensor; 40 | } else { 41 | delete sensor; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/KnightLab_Sensors/src/KnightLab_SensorConfig.h: -------------------------------------------------------------------------------- 1 | #ifndef __KNIGHTLAB__SENSORCONFIG_H 2 | #define __KNIGHTLAB__SENSORCONFIG_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "KnightLab_SensorInterface.h" 9 | #include "KL_ADAFRUIT_SI7021.h" 10 | #include "HONEYWELL_HPM.h" 11 | 12 | #define MAX_SENSOR_ID_STR 30 13 | 14 | extern void loadSensorConfig(uint8_t node_id, TimeFunction time_fcn); 15 | 16 | struct SensorConfig { 17 | SensorInterface *sensor; 18 | struct SensorConfig *next; 19 | }; 20 | 21 | #endif -------------------------------------------------------------------------------- /lib/KnightLab_Sensors/src/KnightLab_SensorInterface.h: -------------------------------------------------------------------------------- 1 | #ifndef __KNIGHTLAB__SENSORINTERFACE_H 2 | #define __KNIGHTLAB__SENSORINTERFACE_H 3 | 4 | #include 5 | #include 6 | 7 | #define MAX_SENSOR_ID_STR 30 8 | 9 | typedef uint32_t (*TimeFunction)(); 10 | 11 | class SensorInterface { 12 | public: 13 | virtual ~SensorInterface(){}; 14 | char *id = 0; 15 | virtual bool setup() = 0; 16 | virtual bool start() = 0; 17 | virtual size_t read(char* buf, int len) = 0; 18 | virtual bool stop() = 0; 19 | }; 20 | 21 | #endif -------------------------------------------------------------------------------- /lib/KnightLab_Sensors/src/base.h: -------------------------------------------------------------------------------- 1 | #ifndef __KNIGHTLAB__BASE_H 2 | #define __KNIGHTLAB__BASE_H 3 | 4 | 5 | typedef uint32_t (*TimeFunction)(); 6 | 7 | class Interface { 8 | public: 9 | virtual bool setup(); 10 | //virtual bool start(); 11 | virtual size_t read(); 12 | //virtual bool stop(); 13 | }; 14 | 15 | #endif -------------------------------------------------------------------------------- /lib/TaskScheduler/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Anatoli Arkhipenko. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 22 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 24 | OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /lib/TaskScheduler/README.md: -------------------------------------------------------------------------------- 1 | # Task Scheduler 2 | ### Cooperative multitasking for Arduino microcontrollers 3 | #### Version 2.6.1: 2018-03-15 4 | 5 | Customized version of Task Scheduler Library from https://github.com/arkhipenko/TaskScheduler. Changes use of millseconds to seconds. 6 | 7 | ### OVERVIEW: 8 | A lightweight implementation of cooperative multitasking (task scheduling) supporting: 9 | 1. Periodic task execution, with dynamic execution period in `milliseconds` (default) or `microseconds` (if explicitly enabled) – frequency of execution 10 | 2. Number of iterations (limited or infinite number of iterations) 11 | 3. Execution of tasks in predefined sequence 12 | 4. Dynamic change of task execution parameters (frequency, number of iterations, callback methods) 13 | 5. Power saving via entering **IDLE** sleep mode when tasks are not scheduled to run 14 | 6. Support for event-driven task invocation via Status Request object 15 | 7. Support for task IDs and Control Points for error handling and watchdog timer 16 | 8. Support for Local Task Storage pointer (allowing use of same callback code for multiple tasks) 17 | 9. Support for layered task prioritization 18 | 10. Support for `std::functions` (`ESP8266` only) 19 | 11. Overall task timeout 20 | 21 | Scheduling overhead: between `15` and `18` microseconds per scheduling pass (Arduino UNO rev 3 @ `16MHz` clock, single scheduler w/o prioritization) 22 | 23 | **TaskScheduler** was tested on the following platforms: 24 | * Arduino Uno R3 25 | * Arduino Nano 26 | * Arduino Micro 27 | * ATtiny85 28 | * ESP8266 (Node MCU v2.0) 29 | * ESP32 30 | * Teensy (tested on Teensy 3.5) 31 | --- 32 | ![TaskScheduler process diagram](https://github.com/arkhipenko/TaskScheduler/raw/master/extras/TaskScheduler_html.png) 33 | --- 34 | ### [Changelog:](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog) 35 | Version|Version 1|Version 2 36 | ---|---|--- 37 | || |[2.6.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v261) (*current version*) 38 | ||[1.9.2](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v192)|[2.6.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v260) 39 | ||[1.9.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v190)|[2.5.2](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v252) 40 | ||[1.8.5](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v185)|[2.5.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v251) 41 | ||[1.8.4](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v184)|[2.5.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v250) 42 | ||[1.8.3](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v183)|[2.4.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v240) 43 | ||[1.8.2](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v182)|[2.3.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v230) 44 | ||[1.8.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v181)|[2.2.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v221) 45 | ||[1.8.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v180)|[2.2.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v220) 46 | ||[1.7.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v170)|[2.1.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v210) 47 | ||[1.6.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v160)|[2.0.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v201) 48 | ||[1.5.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v151)|[2.0.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v200) 49 | ||[1.5.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v150)| 50 | ||[1.4.1](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v141)| 51 | ||[1.0.0](https://github.com/arkhipenko/TaskScheduler/wiki/Changelog#v100)| 52 | 53 | #### For detailed functionality overview please refer to TaskScheduler documentation in the 'extras' folder or in the [Wiki page](https://github.com/arkhipenko/TaskScheduler/wiki). 54 | 55 | ### Check out what TaskScheduler can do: 56 | 57 | * [Blow the Wall!](http://embedio.com.au/projects/LCDgame/index.htm) - a simple computer shooter game using a 16x2 LCD panel, an Arduino, piezo buzzer, WAV player module, and pushbutton switches. 58 | 59 | * [3 Devo](http://3devo.eu/) - Quality 3D printing filament, now made accessible and affordable 60 | (http://3devo.eu/license-information/) 61 | 62 | 63 | * [Houston midi](https://github.com/chaffneue/houston) clock project - TaskScheduler with microseconds resolution 64 | >by chaffneue: 65 | >>My first arduino project. It's a multi-master midi controller with a shared clock and 66 | auto count in behaviour. 67 | 68 | youtube: https://www.youtube.com/watch?v=QRof550TtXo 69 | 70 | 71 | * [Hackabot Nano](http://hackarobot.com/) by Funnyvale - Compact Plug and Play Arduino compatible robotic kit 72 | https://www.kickstarter.com/projects/hackarobot/hackabot-nano-compact-plug-and-play-arduino-robot 73 | 74 | 75 | * Arduino Nano based Hexbug Scarab Robotic Spider 76 | (by arkhipenko: http://www.instructables.com/id/Arduino-Nano-based-Hexbug-Scarab-Robotic-Spider/) 77 | 78 | * Wave your hand to control OWI Robotic Arm... no strings attached 79 | (by arkhipenko: http://www.instructables.com/id/Wave-your-hand-to-control-OWI-Robotic-Arm-no-strin/) 80 | 81 | 82 | * APIS - Automated Plant Irrigation System 83 | (by arkhipenko: http://www.instructables.com/id/APIS-Automated-Plant-Irrigation-System/) 84 | 85 | 86 | * IoT APIS v2 - Autonomous IoT-enabled Automated Plant Irrigation System 87 | (by arkhipenko: http://www.instructables.com/id/IoT-APIS-V2-Autonomous-IoT-enabled-Automated-Plant/) 88 | 89 | * Interactive Halloween Pumpkin 90 | (by arkhipenko: http://www.instructables.com/id/Interactive-Halloween-Pumpkin/) 91 | 92 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example01/Scheduler_example01.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test 3 | * 4 | * Initially only tasks 1 and 2 are enabled 5 | * Task1 runs every 2 seconds 10 times and then stops 6 | * Task2 runs every 3 seconds indefinitely 7 | * Task1 enables Task3 at its first run 8 | * Task3 run every 5 seconds 9 | * Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds 10 | * At the end Task2 is the only task running every 1/2 seconds 11 | */ 12 | 13 | 14 | #include 15 | 16 | // Callback methods prototypes 17 | void t1Callback(); 18 | void t2Callback(); 19 | void t3Callback(); 20 | 21 | //Tasks 22 | Task t4(); 23 | Task t1(2000, 10, &t1Callback); 24 | Task t2(3000, TASK_FOREVER, &t2Callback); 25 | Task t3(5000, TASK_FOREVER, &t3Callback); 26 | 27 | Scheduler runner; 28 | 29 | 30 | void t1Callback() { 31 | Serial.print("t1: "); 32 | Serial.println(millis()); 33 | 34 | if (t1.isFirstIteration()) { 35 | runner.addTask(t3); 36 | t3.enable(); 37 | Serial.println("t1: enabled t3 and added to the chain"); 38 | } 39 | 40 | if (t1.isLastIteration()) { 41 | t3.disable(); 42 | runner.deleteTask(t3); 43 | t2.setInterval(500); 44 | Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); 45 | } 46 | } 47 | 48 | void t2Callback() { 49 | Serial.print("t2: "); 50 | Serial.println(millis()); 51 | 52 | } 53 | 54 | void t3Callback() { 55 | Serial.print("t3: "); 56 | Serial.println(millis()); 57 | 58 | } 59 | 60 | void setup () { 61 | Serial.begin(115200); 62 | Serial.println("Scheduler TEST"); 63 | 64 | runner.init(); 65 | Serial.println("Initialized scheduler"); 66 | 67 | runner.addTask(t1); 68 | Serial.println("added t1"); 69 | 70 | runner.addTask(t2); 71 | Serial.println("added t2"); 72 | 73 | delay(5000); 74 | 75 | t1.enable(); 76 | Serial.println("Enabled t1"); 77 | t2.enable(); 78 | Serial.println("Enabled t2"); 79 | } 80 | 81 | 82 | void loop () { 83 | runner.execute(); 84 | } -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example02/Scheduler_example02.ino: -------------------------------------------------------------------------------- 1 | #define _TASK_SLEEP_ON_IDLE_RUN 2 | #include 3 | 4 | Scheduler runner; 5 | // Callback methods prototypes 6 | void t1Callback(); 7 | void t2Callback(); 8 | void t3Callback(); 9 | 10 | // Tasks 11 | Task t4(); 12 | Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation 13 | Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation 14 | Task t3(5000, TASK_FOREVER, &t3Callback); 15 | 16 | // Test 17 | // Initially only tasks 1 and 2 are enabled 18 | // Task1 runs every 2 seconds 10 times and then stops 19 | // Task2 runs every 3 seconds indefinitely 20 | // Task1 enables Task3 at its first run 21 | // Task3 run every 5 seconds 22 | // loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected) 23 | // Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds 24 | // Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now 25 | // At the end Task2 is the only task running every 1/2 seconds 26 | // 27 | // NOTE that t1 and t2 are affected by the delay() function in the setup() method and are scheduled immediately twice to "catch up" with millis(). 28 | 29 | 30 | 31 | 32 | 33 | void t1Callback() { 34 | Serial.print("t1: "); 35 | Serial.println(millis()); 36 | 37 | if (t1.isFirstIteration()) { 38 | runner.addTask(t3); 39 | t3.enable(); 40 | Serial.println("t1: enabled t3 and added to the chain"); 41 | } 42 | 43 | if (t1.isLastIteration()) { 44 | t3.disable(); 45 | runner.deleteTask(t3); 46 | t2.setInterval(500); 47 | Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); 48 | } 49 | } 50 | 51 | void t2Callback() { 52 | Serial.print("t2: "); 53 | Serial.println(millis()); 54 | 55 | } 56 | 57 | void t3Callback() { 58 | Serial.print("t3: "); 59 | Serial.println(millis()); 60 | 61 | } 62 | 63 | void setup () { 64 | Serial.begin(115200); 65 | Serial.println("Scheduler TEST"); 66 | 67 | delay(5000); 68 | 69 | runner.startNow(); // set point-in-time for scheduling start 70 | } 71 | 72 | 73 | void loop () { 74 | 75 | runner.execute(); 76 | 77 | // Serial.println("Loop ticks at: "); 78 | // Serial.println(millis()); 79 | } -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example03/Scheduler_example03.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test of OnEnable and OnDisable methods and illustration of using wrapper tasks for timout purposes 3 | * 4 | * A wrapper task runs every 10 seconds and initiates the test case 5 | * Another task is run once for 5 seconds, and serves as a LED blinking timeout - 5 seconds 6 | * Finally, a dedicated task which controls LED is running periodically until stopped, and makes the LED blink with 0.5 to 1 second interval. 7 | * 8 | */ 9 | 10 | #define _TASK_SLEEP_ON_IDLE_RUN 11 | #include 12 | 13 | #ifndef LED_BUILTIN 14 | #define LED_BUILTIN 13 // define appropriate pin for your board 15 | #endif 16 | 17 | Scheduler ts; 18 | 19 | // Callback methods prototypes 20 | void WrapperCallback(); 21 | bool BlinkOnEnable(); 22 | void BlinkOnDisable(); 23 | void LEDOn(); 24 | void LEDOff(); 25 | 26 | // Tasks 27 | Task tWrapper(10000L, TASK_FOREVER, &WrapperCallback, &ts, true); 28 | Task tBlink(5000, TASK_ONCE, NULL, &ts, false, &BlinkOnEnable, &BlinkOnDisable); 29 | Task tLED(0, TASK_FOREVER, NULL, &ts, false, NULL, &LEDOff); 30 | 31 | void WrapperCallback() { 32 | tBlink.restartDelayed(); // LED blinking is initiated 33 | //every 30 seconds for 5 seconds 34 | } 35 | 36 | 37 | // Upon being enabled, tBlink will define the parameters 38 | // and enable LED blinking task, which actually controls 39 | // the hardware (LED in this example) 40 | bool BlinkOnEnable() { 41 | tLED.setInterval( 500 + random(501) ); 42 | tLED.setCallback( &LEDOn); 43 | tLED.enable(); 44 | 45 | return true; // Task should be enabled 46 | } 47 | 48 | // tBlink does not really need a callback function 49 | // since it just waits for 5 seconds for the first 50 | // and only iteration to occur. Once the iteration 51 | // takes place, tBlink is disabled by the Scheduler, 52 | // thus executing its OnDisable method below. 53 | 54 | void BlinkOnDisable() { 55 | tLED.disable(); 56 | } 57 | 58 | void LEDOn () { 59 | digitalWrite(LED_BUILTIN , HIGH); 60 | tLED.setCallback( &LEDOff); 61 | } 62 | 63 | void LEDOff () { 64 | digitalWrite(LED_BUILTIN , LOW); 65 | tLED.setCallback( &LEDOn); 66 | } 67 | 68 | // Note that LEDOff method serves as OnDisable method 69 | // to make sure the LED is turned off when the tBlink 70 | // task finishes (or disabled ahead of time) 71 | 72 | void setup() { 73 | // put your setup code here, to run once: 74 | pinMode(LED_BUILTIN , OUTPUT); 75 | } 76 | 77 | void loop() { 78 | // put your main code here, to run repeatedly: 79 | ts.execute(); 80 | } -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example04_StatusRequest/Scheduler_example04_StatusRequest.ino: -------------------------------------------------------------------------------- 1 | /** This test demonstrates interaction between three simple tasks via StatusRequest object. 2 | * Task T1 runs every 5 seconds and signals completion of a status request st. 3 | * Tasks T2 and T3 are waiting on the same request (st) 4 | * Task T3 does not renew its interest in status request st, so it is only invoked once (first iteration) 5 | * Task T2 is invoked every time st completes, because it renews its interest in status of status request object st every iteration of T1 6 | */ 7 | 8 | #define _TASK_SLEEP_ON_IDLE_RUN 9 | #define _TASK_STATUS_REQUEST 10 | #include 11 | 12 | StatusRequest st; 13 | 14 | Scheduler ts; 15 | 16 | // Callback methods prototypes 17 | void Callback1(); 18 | void Disable1(); 19 | void Callback2(); 20 | void Callback3(); 21 | void PrepareStatus(); 22 | 23 | // Tasks 24 | Task t1(5000, TASK_ONCE, &Callback1, &ts, true, NULL, &Disable1); 25 | Task t2(&Callback2, &ts); 26 | Task t3(&Callback3, &ts); 27 | 28 | /** T1 callback 29 | * T1 just signals completion of st every 5 seconds 30 | */ 31 | void Callback1() { 32 | Serial.println("T1: Signaling completion of ST"); 33 | st.signalComplete(); 34 | } 35 | 36 | /** T1 On Disable callback 37 | * This callback renews the status request and restarts T1 delayed to run again in 5 seconds 38 | */ 39 | void Disable1() { 40 | PrepareStatus(); 41 | t1.restartDelayed(); 42 | } 43 | 44 | /** T2 callback 45 | * Invoked when status request st completes 46 | */ 47 | void Callback2() { 48 | Serial.println("T2: Invoked due to completion of ST"); 49 | } 50 | 51 | 52 | /** T3 callback 53 | * Invoked when status request st completes. 54 | * This is only run once since T3 does not renew its interest in the status request st after first iteration 55 | */ 56 | void Callback3() { 57 | Serial.println("T3: Invoked due to completion of ST"); 58 | 59 | } 60 | 61 | /** Prepare Status request st for another iteration 62 | * 63 | */ 64 | void PrepareStatus() { 65 | st.setWaiting(); // set the statusrequest object for waiting 66 | t2.waitFor(&st); // request tasks 1 & 2 to wait on the object st 67 | } 68 | 69 | 70 | /** Main Arduino code 71 | * Not much to do here. Just init Serial and set the initial status request 72 | */ 73 | void setup() { 74 | 75 | Serial.begin(115200); 76 | Serial.println("TaskScheduler: Status Request Test 1. Simple Test."); 77 | 78 | PrepareStatus(); 79 | t3.waitFor(&st); 80 | 81 | t1.delay(); 82 | } 83 | 84 | void loop() { 85 | 86 | ts.execute(); 87 | 88 | } -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example05_StatusRequest/Scheduler_example05_StatusRequest.ino: -------------------------------------------------------------------------------- 1 | /** This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay 2 | * (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values. 3 | * The overall timeout of 1 second is setup as well. 4 | * An error message needs to be printed if a timeout occurred instead of a value. 5 | */ 6 | 7 | 8 | #define _TASK_SLEEP_ON_IDLE_RUN 9 | #define _TASK_STATUS_REQUEST 10 | #include 11 | 12 | StatusRequest measure; 13 | 14 | Scheduler ts; 15 | 16 | // Callback methods prototypes 17 | void CycleCallback(); 18 | void MeasureCallback(); 19 | bool MeasureEnable(); 20 | void MeasureDisable(); 21 | void CalcCallback(); 22 | void S1Callback(); bool S1Enable(); 23 | void S2Callback(); bool S2Enable(); 24 | void S3Callback(); bool S3Enable(); 25 | 26 | // Tasks 27 | Task tCycle(10000, TASK_FOREVER, &CycleCallback, &ts, true); 28 | Task tMeasure(1000, TASK_ONCE, &MeasureCallback, &ts, false, &MeasureEnable, &MeasureDisable); 29 | Task tCalculate(&CalcCallback, &ts); 30 | Task tSensor1(0, TASK_ONCE, &S1Callback, &ts, false, &S1Enable); 31 | Task tSensor2(0, TASK_ONCE, &S2Callback, &ts, false, &S2Enable); 32 | Task tSensor3(0, TASK_ONCE, &S3Callback, &ts, false, &S3Enable); 33 | 34 | 35 | long distance, d1, d2, d3; 36 | 37 | void CycleCallback() { 38 | Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); 39 | 40 | tMeasure.restartDelayed(); 41 | } 42 | 43 | 44 | 45 | bool MeasureEnable() { 46 | Serial.println("MeasureEnable: Activating sensors"); 47 | 48 | distance = 0; 49 | measure.setWaiting(3); // Set the StatusRequest to wait for 3 signals. 50 | tCalculate.waitFor(&measure); 51 | 52 | tSensor1.restartDelayed(); 53 | tSensor2.restartDelayed(); 54 | tSensor3.restartDelayed(); 55 | 56 | return true; 57 | } 58 | 59 | void MeasureCallback() { 60 | Serial.println("MeasureCallback: Invoked by calculate task or one second later"); 61 | 62 | if (measure.pending()) { 63 | tCalculate.disable(); 64 | measure.signalComplete(-1); // signal error 65 | Serial.println("MeasureCallback: Timeout!"); 66 | } 67 | else { 68 | Serial.print("MeasureCallback: Min distance=");Serial.println(distance); 69 | } 70 | } 71 | 72 | void MeasureDisable() { 73 | Serial.println("MeasureDisable: Cleaning up"); 74 | 75 | tSensor1.disable(); 76 | tSensor2.disable(); 77 | tSensor3.disable(); 78 | } 79 | 80 | 81 | void CalcCallback() { 82 | Serial.println("CalcCallback: calculating"); 83 | distance = -1; 84 | if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully 85 | distance = d1 < d2 ? d1 : d2; 86 | distance = d3 < distance ? d3 : distance; 87 | tMeasure.forceNextIteration(); 88 | } 89 | } 90 | 91 | 92 | /** Simulation code for sensor 1 93 | * ---------------------------- 94 | */ 95 | bool S1Enable() { 96 | Serial.print("S1Enable: Triggering sensor1. Delay="); 97 | 98 | tSensor1.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout 99 | d1 = 0; 100 | 101 | Serial.println( tSensor1.getInterval() ); 102 | return true; 103 | } 104 | 105 | void S1Callback() { 106 | Serial.print("S1Callback: Emulating measurement. d1="); 107 | d1 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 108 | measure.signal(); 109 | 110 | Serial.println(d1); 111 | } 112 | 113 | 114 | /** Simulation code for sensor 2 115 | * ---------------------------- 116 | */ 117 | bool S2Enable() { 118 | Serial.print("S2Enable: Triggering sensor2. Delay="); 119 | 120 | tSensor2.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout 121 | d2 = 0; 122 | 123 | Serial.println( tSensor2.getInterval() ); 124 | return true; 125 | } 126 | 127 | void S2Callback() { 128 | Serial.print("S2Callback: Emulating measurement. d2="); 129 | d2 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 130 | measure.signal(); 131 | 132 | Serial.println(d2); 133 | } 134 | 135 | 136 | /** Simulation code for sensor 3 137 | * ---------------------------- 138 | */ 139 | bool S3Enable() { 140 | Serial.print("S3Enable: Triggering sensor3. Delay="); 141 | 142 | tSensor3.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout 143 | d3 = 0; 144 | 145 | Serial.println( tSensor3.getInterval() ); 146 | return true; 147 | } 148 | 149 | void S3Callback() { 150 | Serial.print("S3Callback: Emulating measurement. d3="); 151 | d3 = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 152 | measure.signal(); 153 | 154 | Serial.println(d3); 155 | } 156 | 157 | 158 | /** Main Arduino code 159 | * Not much is left here - everything is taken care of by the framework 160 | */ 161 | void setup() { 162 | 163 | Serial.begin(115200); 164 | Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); 165 | randomSeed(analogRead(A0)+millis()); 166 | } 167 | 168 | void loop() { 169 | 170 | ts.execute(); 171 | 172 | } 173 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example06_IDLE/Scheduler_example06_IDLE.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This is a test to prove that processor really goes into IDLE sleep. 4 | * For this setup: 5 | * 6 | * 7 | 8 | Task c(10, -1, &Count, &ts); 9 | Task t(10000, 1, NULL, &ts, true, &tOn, &tOff); 10 | 11 | The result are: 12 | 13 | 1): With #define _TASK_SLEEP_ON_IDLE_RUN enabled 14 | On Arduino Uno: 15 | Start 16 | c1=10771 - v2.5.0 (v1.9.0: same) 17 | c2=1001 18 | 19 | On Teensy 3.5 (120MHz ARM): 20 | Start 21 | c1=21065 22 | c2=1001 23 | 24 | and 25 | 26 | 2): With #define _TASK_SLEEP_ON_IDLE_RUN disabled (commented out) 27 | Arduino Uno: 28 | Start 29 | c1=635735 - v2.5.0 (v1.9.0: 551947) 30 | c2=1001 31 | 32 | On Teensy 3.5 (120MHz ARM): 33 | Start 34 | c1=2690322 35 | c2=1001 36 | 37 | C1 is scenario 2) is much higher than in scenario 1) because processor is put to sleep for 1), but not for 2) 38 | 39 | */ 40 | 41 | 42 | /** 43 | * Compile and run once with _TASK_SLEEP_ON_IDLE_RUN enabled, then with _TASK_SLEEP_ON_IDLE_RUN disabled. 44 | * Compare the results. 45 | */ 46 | 47 | //#define _TASK_SLEEP_ON_IDLE_RUN 48 | #include 49 | 50 | Scheduler ts; 51 | 52 | // Callback methods prototypes 53 | void Count(); 54 | bool tOn(); void tOff(); 55 | 56 | // Tasks 57 | Task c(10, TASK_FOREVER, &Count, &ts); 58 | Task t(10000, TASK_ONCE, NULL, &ts, true, &tOn, &tOff); 59 | 60 | 61 | volatile unsigned long c1, c2; 62 | bool tOn() { 63 | c1 = 0; 64 | c2 = 0; 65 | c.enable(); 66 | 67 | return true; 68 | } 69 | 70 | void tOff() { 71 | c.disable(); 72 | Serial.print("c1=");Serial.println(c1); 73 | Serial.print("c2=");Serial.println(c2); 74 | } 75 | 76 | void setup() { 77 | // put your setup code here, to run once: 78 | Serial.begin(115200); 79 | Serial.println("Start"); 80 | t.delay(); 81 | // ts.allowSleep(false); 82 | } 83 | 84 | void Count() { 85 | c2++; 86 | } 87 | 88 | 89 | void loop() { 90 | // put your main code here, to run repeatedly: 91 | ts.execute(); 92 | c1++; 93 | } -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example07_WDT/Scheduler_example07_WDT.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test sketch - use of task IDs and watchdog timer to identify hung tasks 3 | * Test case: 4 | * Watchdog timer is set to 2 seconds (interrupt + reset) 5 | * A hearbeat task (resetting the watchdog timer) is scheduled with 500 ms interval 6 | * A number of tasks are running every 1 second and "rolling the dice" 0..19. If 5, task is made to enter infinite loop 7 | * Device should reset in 2 seconds after a task enters infinite loop 8 | * A task id and a control point number are saved to EEPROM prior to device reset, and are displayed after reboot. 9 | * In real life, device might chose to NOT activate certain tasks which failed previously (failed sensors for instance) 10 | */ 11 | 12 | #define _TASK_SLEEP_ON_IDLE_RUN 13 | #define _TASK_WDT_IDS 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | Scheduler ts; 20 | 21 | // Callback methods prototypes 22 | void TaskCB(); 23 | void HB(); bool HBOn(); void HBOff(); 24 | 25 | // Three tasks emulating accidental infinite loop 26 | Task tTask1(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); 27 | Task tTask2(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); 28 | Task tTask3(TASK_SECOND, TASK_FOREVER, &TaskCB, &ts, true); 29 | 30 | // Heartbeat task - resetting the watchdog timer periodically 31 | // Initiates WDT on enable, and deactivates it on disable 32 | Task tHB(500, TASK_FOREVER, &HB, &ts, false, &HBOn, &HBOff); 33 | 34 | /** 35 | * Emulating task callback function 36 | * Prints task id and randomly "hangs" in two places. 37 | * Control points are stored on the task prior to section which might hang, 38 | * making this information available to the WDT interrupt handler 39 | */ 40 | void TaskCB() { 41 | Task& T = ts.currentTask(); 42 | 43 | Serial.print("Task #:"); 44 | Serial.print(T.getId()); 45 | Serial.print(" current iteration = "); 46 | Serial.println(T.getRunCounter()); 47 | 48 | // Hang if random number between 0 and 19 is 5 (5% probability) 49 | T.setControlPoint(10); 50 | if (random(20) == 5) for(;;); 51 | 52 | // Hang if random number between 0 and 99 is more that 95 (5% probability) 53 | T.setControlPoint(95); 54 | if (random(100) > 94) for(;;); 55 | } 56 | 57 | /** 58 | * This On Enable method sets up the WDT 59 | * for interrupt and reset after 2 seconds 60 | */ 61 | bool HBOn() { 62 | 63 | //disable interrupts 64 | cli(); 65 | //reset watchdog 66 | wdt_reset(); 67 | //set up WDT interrupt 68 | WDTCSR = (1<id); 85 | Serial.print("Sum: "); Serial.println(vars[i]->sum); 86 | Serial.print("Product: "); Serial.println(vars[i]->product); 87 | Serial.println(); 88 | } 89 | } 90 | 91 | 92 | /** 93 | * This method is executed when each calculator task is enabled 94 | * The purpose is to initiate all local variables 95 | */ 96 | bool CalcOn() { 97 | Task& T = ts.currentTask(); 98 | task_var& var = *((task_var*) T.getLtsPointer()); 99 | 100 | // Initialize local variables 101 | var.id = T.getId(); 102 | var.sum = 0; 103 | var.product = var.id; 104 | 105 | return true; 106 | } 107 | 108 | 109 | /** 110 | * This method performs simple calculations on task's local variables 111 | */ 112 | void Calculate() { 113 | Task& T = ts.currentTask(); 114 | // Another way to get to LTS pointer: 115 | task_var& var = *((task_var*) ts.currentLts()); 116 | 117 | 118 | Serial.print("Calculating for task: "); 119 | Serial.print(T.getId()); 120 | Serial.print("; Task id per LTS is: "); 121 | Serial.println( var.id ); 122 | 123 | var.sum += T.getId(); 124 | var.product = var.product * 10; 125 | 126 | } 127 | 128 | 129 | /** 130 | * Standard Arduino setup and loop methods 131 | */ 132 | void setup() { 133 | Serial.begin(115200); 134 | 135 | randomSeed(analogRead(0)+analogRead(5)); 136 | 137 | pinMode(13, OUTPUT); 138 | digitalWrite(13, LOW); 139 | 140 | Serial.println("Local Task Storage pointer test"); 141 | 142 | tWrapper.enableDelayed(); 143 | } 144 | 145 | void loop() { 146 | ts.execute(); 147 | } -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example09_TimeCritical/Scheduler_example09_TimeCritical.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test 3 | * Illustration of use of Time Critical Information 4 | * 5 | * Task1 runs every 1 second indefinitely 6 | * On each run it reports how delayed the invokation of the callback method was, 7 | * and what was the scheduling overun. 8 | * Each run task 1 is dealyed randomly for up to 2 seconds, thus simulating scheduling overrun 9 | */ 10 | 11 | #define _TASK_TIMECRITICAL 12 | #define _TASK_SLEEP_ON_IDLE_RUN 13 | #include 14 | 15 | // Callback methods prototypes 16 | void t1Callback(); 17 | 18 | 19 | //Tasks 20 | Task t1(1000, -1, &t1Callback); 21 | 22 | Scheduler runner; 23 | 24 | 25 | void t1Callback() { 26 | Serial.print(millis()); 27 | Serial.print(": overrun = "); 28 | Serial.print(t1.getOverrun()); 29 | Serial.print(", start delayed by "); 30 | Serial.println(t1.getStartDelay()); 31 | 32 | int i = random(2000); 33 | Serial.print("Delaying for "); Serial.println(i); 34 | delay(i); 35 | } 36 | 37 | void setup () { 38 | Serial.begin(115200); 39 | Serial.println("Scheduler TimeCritical TEST"); 40 | 41 | runner.init(); 42 | Serial.println("Initialized scheduler"); 43 | 44 | runner.addTask(t1); 45 | Serial.println("added t1. Waiting for 5 seconds."); 46 | 47 | delay(5000); 48 | 49 | t1.enable(); 50 | 51 | Serial.println("Enabled t1"); 52 | } 53 | 54 | 55 | void loop () { 56 | runner.execute(); 57 | } 58 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example10_Benchmark/Scheduler_example10_Benchmark.ino: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * This is a test to benchmark TaskScheduler execution. 4 | * 5 | * This test executes 1,000,000 cycles of a task with empty callback method 6 | * Compiled with different options, you can assess the impact of each on the size of the Task object 7 | * and the execution overhead of the main execution pass route. 8 | * 9 | * Sample execution times (in milliseconds per 1M iterations) are provided below. 10 | * The test board is Arduino UNO 16MHz processor. 11 | * 12 | 13 | TaskScheduler 2.1.0: 14 | No modifiers 15 | Duration=19869 16 | 17 | with SLEEP 18 | Duration=20058 19 | 20 | with status request: 21 | Duration=20058 22 | 23 | with time critical: 24 | Duration=27289 25 | 26 | 27 | TaskScheduler 1.9.0: 28 | No modifiers 29 | Duration=15656 30 | 31 | with SLEEP 32 | Duration=16285 33 | 34 | with status request: 35 | Duration=16600 36 | 37 | with rollover fix: 38 | Duration=18109 39 | 40 | 41 | TaskScheduler 1.8.5: 42 | Duration=15719 43 | 44 | with SLEEP 45 | Duration=16348 46 | 47 | with status request: 48 | Duration=18360 49 | 50 | with rollover fix: 51 | Duration=18423 52 | 53 | */ 54 | 55 | 56 | //#define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 57 | //#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 58 | //#define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 59 | //#define _TASK_LTS_POINTER // Compile with support for local task storage pointer 60 | //#define _TASK_SLEEP_ON_IDLE_RUN 61 | //#define _TASK_MICRO_RES 62 | #include 63 | 64 | Scheduler ts; 65 | 66 | // Callback methods prototypes 67 | bool tOn(); void tOff(); 68 | void callback(); 69 | 70 | // Tasks 71 | Task t(TASK_IMMEDIATE, 1000000, &callback, &ts, false, &tOn, &tOff); 72 | 73 | unsigned long c1, c2; 74 | 75 | bool tOn() { 76 | c1 = millis(); 77 | c2 = 0; 78 | 79 | return true; 80 | } 81 | 82 | void tOff() { 83 | c2 = millis(); 84 | Serial.println("done."); 85 | Serial.print("Tstart =");Serial.println(c1); 86 | Serial.print("Tfinish=");Serial.println(c2); 87 | Serial.print("Duration=");Serial.println(c2-c1); 88 | } 89 | 90 | void setup() { 91 | // put your setup code here, to run once: 92 | Serial.begin(115200); 93 | Serial.print("Start..."); 94 | 95 | t.enable(); 96 | } 97 | 98 | void callback() { 99 | 100 | } 101 | 102 | 103 | void loop() { 104 | // put your main code here, to run repeatedly: 105 | ts.execute(); 106 | } 107 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example11_Priority/Scheduler_example11_Priority.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a test of TaskScheduler layered priority funtionality 3 | * 4 | * Current test employs two priority layers: 5 | * Base scheduler runs tasks t1, t2 and t3 6 | * High priority scheduler runs tasks t4 and t5 7 | * 8 | * Sequence of task scheduling (not execution!) is: 9 | * 4, 5, 1, 4, 5, 2, 4, 5, 3 = one base scheduler pass 10 | * 11 | * Scheduling overhead (at 20 micros per one pass) is: (B + B * H) * T = (3 + 3 * 2) * 18 = 162 micros 12 | * where 13 | * B - number of tasks in the base scheduler's chain 14 | * H - number of tasks in the high priority scheduler's chain 15 | * T - scheduling overhead for 1 pass (~15-18 microseconds) 16 | * 17 | * Actual task execution order: 18 | 19 | Scheduler Priority Test 20 | Task: 40: 0 Start delay = 0 21 | Task: 50: 10 Start delay = 10 22 | Task: 1: 21 Start delay = 21 23 | Task: 2: 31 Start delay = 31 24 | Task: 3: 41 Start delay = 41 25 | 26 | Task: 40: 500 Start delay = 0 27 | Task: 40: 1000 Start delay = 0 28 | Task: 50: 1010 Start delay = 10 29 | Task: 1: 1021 Start delay = 20 30 | Task: 40: 1500 Start delay = 0 31 | Task: 40: 2000 Start delay = 0 32 | Task: 50: 2011 Start delay = 11 33 | Task: 1: 2022 Start delay = 21 34 | Task: 2: 2032 Start delay = 32 35 | Task: 40: 2500 Start delay = 0 36 | Task: 40: 3000 Start delay = 0 37 | Task: 50: 3010 Start delay = 10 38 | Task: 1: 3021 Start delay = 20 39 | Task: 3: 3032 Start delay = 32 40 | 41 | Task: 40: 3500 Start delay = 0 42 | Task: 40: 4000 Start delay = 0 43 | Task: 50: 4011 Start delay = 11 44 | Task: 1: 4022 Start delay = 21 45 | Task: 2: 4032 Start delay = 32 46 | Task: 40: 4500 Start delay = 0 47 | Task: 40: 5000 Start delay = 0 48 | Task: 50: 5010 Start delay = 10 49 | Task: 1: 5021 Start delay = 20 50 | Task: 40: 5500 Start delay = 0 51 | Task: 40: 6000 Start delay = 0 52 | Task: 50: 6010 Start delay = 10 53 | Task: 1: 6022 Start delay = 21 54 | Task: 2: 6032 Start delay = 32 55 | Task: 3: 6043 Start delay = 42 56 | 57 | */ 58 | 59 | #define _TASK_SLEEP_ON_IDLE_RUN 60 | #define _TASK_PRIORITY 61 | #define _TASK_WDT_IDS 62 | #define _TASK_TIMECRITICAL 63 | #include 64 | 65 | Scheduler r, hpr; 66 | 67 | // Callback methods prototypes 68 | void tCallback(); 69 | 70 | // Tasks 71 | Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation 72 | Task t2(2000, TASK_FOREVER, &tCallback, &r); 73 | Task t3(3000, TASK_FOREVER, &tCallback, &r); 74 | 75 | Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation 76 | Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation 77 | 78 | void tCallback() { 79 | Scheduler &s = Scheduler::currentScheduler(); 80 | Task &t = s.currentTask(); 81 | 82 | Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t"); 83 | Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay()); 84 | delay(10); 85 | 86 | if (t.getId() == 3) Serial.println(); 87 | } 88 | 89 | void setup () { 90 | Serial.begin(115200); 91 | Serial.println("Scheduler Priority Test"); 92 | 93 | t4.setId(40); 94 | t5.setId(50); 95 | 96 | r.setHighPriorityScheduler(&hpr); 97 | r.enableAll(true); // this will recursively enable the higher priority tasks as well 98 | } 99 | 100 | 101 | void loop () { 102 | 103 | r.execute(); 104 | 105 | } 106 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example12_Priority/Scheduler_example12_Priority.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a test of TaskScheduler layered priority funtionality 3 | * 4 | * Current test employs three priority layers: 5 | * Base scheduler runs tasks t1, t2 and t3 6 | * High priority scheduler runs tasks t4 and t5 7 | * Highest priority scheduler runs tasks t6 and t7 8 | * 9 | * Sequence of task scheduling (not execution!) is: 10 | * 6, 7, 4, 6, 7, 5, 1, 6, 7, 4, 6, 7, 5, 2, 6, 7, 4, 6, 7, 5, 3 = one base scheduler pass 11 | * 12 | * Scheduling overhead (at 20 micros per one pass) is: (B + B * H + B * H * C) * T = (3 + 3 * 2 + 3 * 2 * 2) * 18 = 378 micros 13 | * where 14 | * B - number of tasks in the base scheduler's chain 15 | * H - number of tasks in the high priority scheduler's chain 16 | * C - number of tasks in the critical priority scheduler's chain 17 | * T - scheduling overhead for 1 pass (~15-18 microseconds) 18 | * 19 | * Actual task execution order: 20 | 21 | Scheduler Priority Test 22 | Task: 600: 0 Start delay = 0 23 | Task: 700: 10 Start delay = 10 24 | Task: 40: 21 Start delay = 21 25 | Task: 50: 31 Start delay = 31 26 | Task: 1: 43 Start delay = 41 27 | Task: 2: 53 Start delay = 53 28 | Task: 3: 63 Start delay = 63 29 | 30 | Task: 600: 500 Start delay = 0 31 | Task: 40: 510 Start delay = 10 32 | Task: 600: 1000 Start delay = 0 33 | Task: 700: 1010 Start delay = 10 34 | Task: 40: 1021 Start delay = 21 35 | Task: 50: 1032 Start delay = 32 36 | Task: 1: 1043 Start delay = 43 37 | Task: 600: 1500 Start delay = 0 38 | Task: 40: 1510 Start delay = 10 39 | Task: 600: 2000 Start delay = 0 40 | Task: 700: 2011 Start delay = 11 41 | Task: 40: 2022 Start delay = 22 42 | Task: 50: 2032 Start delay = 32 43 | Task: 1: 2043 Start delay = 43 44 | Task: 2: 2054 Start delay = 54 45 | Task: 600: 2500 Start delay = 0 46 | Task: 40: 2510 Start delay = 10 47 | Task: 600: 3000 Start delay = 0 48 | Task: 700: 3010 Start delay = 10 49 | Task: 40: 3021 Start delay = 21 50 | Task: 50: 3032 Start delay = 32 51 | Task: 1: 3043 Start delay = 43 52 | Task: 3: 3053 Start delay = 53 53 | 54 | Task: 600: 3500 Start delay = 0 55 | Task: 40: 3510 Start delay = 10 56 | Task: 600: 4000 Start delay = 0 57 | Task: 700: 4011 Start delay = 11 58 | Task: 40: 4022 Start delay = 22 59 | Task: 50: 4032 Start delay = 32 60 | Task: 1: 4043 Start delay = 43 61 | Task: 2: 4054 Start delay = 54 62 | Task: 600: 4500 Start delay = 0 63 | Task: 40: 4510 Start delay = 10 64 | Task: 600: 5000 Start delay = 0 65 | Task: 700: 5010 Start delay = 10 66 | Task: 40: 5021 Start delay = 21 67 | Task: 50: 5031 Start delay = 31 68 | Task: 1: 5043 Start delay = 43 69 | Task: 600: 5500 Start delay = 0 70 | Task: 40: 5511 Start delay = 11 71 | Task: 600: 6000 Start delay = 0 72 | Task: 700: 6010 Start delay = 10 73 | Task: 40: 6022 Start delay = 22 74 | Task: 50: 6032 Start delay = 32 75 | Task: 1: 6043 Start delay = 43 76 | Task: 2: 6053 Start delay = 53 77 | Task: 3: 6065 Start delay = 65 78 | 79 | */ 80 | 81 | #define _TASK_SLEEP_ON_IDLE_RUN 82 | #define _TASK_PRIORITY 83 | #define _TASK_WDT_IDS 84 | #define _TASK_TIMECRITICAL 85 | #include 86 | 87 | Scheduler r; 88 | Scheduler hpr; 89 | Scheduler cpr; 90 | 91 | // Callback methods prototypes 92 | void tCallback(); 93 | 94 | // Tasks 95 | Task t1(1000, TASK_FOREVER, &tCallback, &r); //adding task to the chain on creation 96 | Task t2(2000, TASK_FOREVER, &tCallback, &r); 97 | Task t3(3000, TASK_FOREVER, &tCallback, &r); 98 | 99 | Task t4(500, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation 100 | Task t5(1000, TASK_FOREVER, &tCallback, &hpr); //adding task to the chain on creation 101 | 102 | Task t6(500, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation 103 | Task t7(1000, TASK_FOREVER, &tCallback, &cpr); //adding task to the chain on creation 104 | 105 | void tCallback() { 106 | Scheduler &s = Scheduler::currentScheduler(); 107 | Task &t = s.currentTask(); 108 | 109 | Serial.print("Task: "); Serial.print(t.getId());Serial.print(":\t"); 110 | Serial.print(millis()); Serial.print("\tStart delay = "); Serial.println(t.getStartDelay()); 111 | delay(10); 112 | 113 | if (t.getId() == 3) Serial.println(); 114 | } 115 | 116 | void setup () { 117 | Serial.begin(115200); 118 | Serial.println("Scheduler Priority Test"); 119 | 120 | t4.setId(40); 121 | t5.setId(50); 122 | 123 | t6.setId(600); 124 | t7.setId(700); 125 | 126 | r.setHighPriorityScheduler(&hpr); 127 | hpr.setHighPriorityScheduler(&cpr); 128 | r.enableAll(true); // this will recursively enable the higher priority tasks as well 129 | } 130 | 131 | 132 | void loop () { 133 | 134 | r.execute(); 135 | 136 | } 137 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example13_Micros/Scheduler_example13_Micros.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test of microsecond scheduling resolution 3 | * 4 | * Task 1 runs starting with 211 microseconds intervals, doubling the interval on every iteration 5 | * until it wraps when interval reaches about 72 minutes mark 6 | * 7 | * Task 2 provides heartbeat at a steady 5 seconds intervals 8 | * 9 | */ 10 | 11 | #define _TASK_MICRO_RES 12 | #include 13 | 14 | #define T1_INIT (211L) 15 | 16 | Scheduler runner; 17 | // Callback methods prototypes 18 | void t1Callback(); 19 | void t1OnDisable(); 20 | void t2Callback(); 21 | 22 | 23 | unsigned long t1_interval = T1_INIT; 24 | 25 | // Tasks 26 | Task t1(t1_interval, 1, &t1Callback, &runner, true, NULL, &t1OnDisable); //adding task to the chain on creation 27 | Task t2(5 * TASK_SECOND, TASK_FOREVER, &t2Callback, &runner, true); //adding task to the chain on creation 28 | 29 | 30 | 31 | void t1Callback() { 32 | unsigned long t = micros(); 33 | Serial.print("t1: "); 34 | Serial.println(t); 35 | } 36 | 37 | void t1OnDisable() { 38 | t1_interval += t1_interval; 39 | if (t1_interval < T1_INIT) t1_interval = T1_INIT; 40 | t1.setInterval(t1_interval); 41 | t1.restartDelayed(); 42 | } 43 | 44 | void t2Callback() { 45 | unsigned long t = micros(); 46 | Serial.print("t2: "); 47 | Serial.print(t); 48 | Serial.println(" heartbeat"); 49 | } 50 | 51 | 52 | void setup () { 53 | Serial.begin(115200); 54 | Serial.println("Scheduler TEST Microsecond Resolution"); 55 | 56 | Serial.println("5 seconds delay"); 57 | delay(5000); 58 | 59 | runner.startNow(); // This creates a new scheduling starting point for all ACTIVE tasks. 60 | // PLEASE NOTE - THIS METHOD DOES NOT ACTIVATE TASKS, JUST RESETS THE START TIME 61 | t1.delay(); // Tasks which need to start delayed, need to be delayed again after startNow(); 62 | 63 | // Alternatively, tasks should be just enabled at the bottom of setup() method 64 | // runner.enableAll(); 65 | // t1.delay(); 66 | } 67 | 68 | 69 | void loop () { 70 | runner.execute(); 71 | } 72 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example15_STDFunction/Scheduler_example15_STDFunction.ino: -------------------------------------------------------------------------------- 1 | /** 2 | * TaskScheduler Test sketch - Showing how to use std::function 3 | * to get acces to variables from within the task callback function 4 | * 5 | * Support for std::function is only available for ESP8266 architecture 6 | */ 7 | #define _TASK_SLEEP_ON_IDLE_RUN 8 | #define _TASK_STD_FUNCTION // Compile with support for std::function 9 | #include 10 | 11 | Scheduler ts; 12 | int counter = 0; 13 | 14 | class Calculator { 15 | public: 16 | int cumSum = 0; // cumulative sum 17 | Calculator(int b) { 18 | // Pass the this pointer, so that we get access to this->cumSum 19 | // Also pass a copy of b 20 | calculateTask.set(TASK_SECOND, TASK_FOREVER, [this, b]() { 21 | counter++; 22 | Serial.printf("%u. %u: cumSum = %u + %u\t", counter, millis(), cumSum, b); 23 | cumSum += b; 24 | Serial.printf("Resulting cumulative sum: %u\n", cumSum); 25 | }); 26 | ts.addTask(calculateTask); 27 | calculateTask.enable(); 28 | } 29 | 30 | Task calculateTask; 31 | }; 32 | 33 | Calculator calc1(2); 34 | Calculator calc2(4); 35 | Calculator calc3(8); 36 | 37 | // Disable tasks after 10 seconds 38 | Task tWrapper(10*TASK_SECOND, TASK_ONCE, []() { 39 | ts.disableAll(); 40 | }, &ts); 41 | 42 | /** 43 | * Standard Arduino setup and loop methods 44 | */ 45 | void setup() { 46 | Serial.begin(74880); 47 | Serial.println("std::function test"); 48 | tWrapper.enableDelayed(); 49 | } 50 | 51 | void loop() { 52 | ts.execute(); 53 | } -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example16_Multitab/Scheduler_example16_Multitab.ino: -------------------------------------------------------------------------------- 1 | //This file is intentionally left blank. 2 | // 3 | //Arduino IDE plays some dirty tricks on the main sketch .ino file: 4 | //it rearranges #includes, blindly creates forward definitions, 5 | //includes every file in the project that does not have .c or .cpp 6 | //file extension. 7 | // 8 | //Usually it all turns well if you have only one source file and you are either 9 | //inexperienced or really expert C++ Arduino programmer. 10 | //For the folks with the middle ground skills level, when you want 11 | //to split your code into several .cpp files, it is best to leave 12 | //this main sketch empty. 13 | // 14 | //It doesn't matter where you define the void loop() and void setup(). 15 | //Just make sure there is exactly one definition of each. 16 | // 17 | //And if you want to use standard Arduino functions 18 | //like digitalWrite or the Serial object - just add #include. 19 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example16_Multitab/file1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "header.hpp" 3 | 4 | 5 | //Declare the functions we want to use before we are ready to define them 6 | void t1Callback(); 7 | 8 | 9 | // Tasks 10 | Task t1(2000, 10, &t1Callback, &runner, true); //adding task to the chain on creation 11 | Task t3(5000, TASK_FOREVER, &t3Callback); 12 | 13 | 14 | void t1Callback() { 15 | Serial.print("t1: "); 16 | Serial.println(millis()); 17 | 18 | if (t1.isFirstIteration()) { 19 | runner.addTask(t3); 20 | t3.enable(); 21 | Serial.println("t1: enabled t3 and added to the chain"); 22 | } 23 | 24 | if (t1.isLastIteration()) { 25 | t3.disable(); 26 | runner.deleteTask(t3); 27 | t2.setInterval(500); 28 | Serial.println("t1: disable t3 and delete it from the chain. t2 interval set to 500"); 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example16_Multitab/file2.cpp: -------------------------------------------------------------------------------- 1 | // Test the same as example#2: 2 | // Initially only tasks 1 and 2 are enabled 3 | // Task1 runs every 2 seconds 10 times and then stops 4 | // Task2 runs every 3 seconds indefinitely 5 | // Task1 enables Task3 at its first run 6 | // Task3 run every 5 seconds 7 | // loop() runs every 1 second (a default scheduler delay, if no shorter tasks' interval is detected) 8 | // Task1 disables Task3 on its last iteration and changed Task2 to run every 1/2 seconds 9 | // Because Task2 interval is shorter than Scheduler default tick, loop() executes ecery 1/2 seconds now 10 | // At the end Task2 is the only task running every 1/2 seconds 11 | 12 | 13 | //Header that declares all shared objects between .cpp files 14 | #include "header.hpp" 15 | 16 | #include //for Serial and delay 17 | 18 | Scheduler runner; //Let the scheduler live here, in the main file, ok? 19 | 20 | 21 | //Pretend, that the t2 task is a special task, 22 | //that needs to live in file2 object file. 23 | void t2Callback() { 24 | Serial.print("t2: "); 25 | Serial.println(millis()); 26 | } 27 | Task t2(3000, TASK_FOREVER, &t2Callback, &runner, true); 28 | 29 | //Lets define t3Callback here. We are going to use it in file1 30 | //for Task 1. 31 | void t3Callback() { 32 | Serial.print("t3: "); 33 | Serial.println(millis()); 34 | } 35 | 36 | 37 | 38 | 39 | 40 | 41 | void setup () { 42 | Serial.begin(115200); 43 | Serial.println("Scheduler TEST"); 44 | 45 | delay(5000); 46 | 47 | runner.startNow(); // set point-in-time for scheduling start 48 | } 49 | 50 | 51 | void loop () { 52 | runner.execute(); 53 | 54 | // Serial.println("Loop ticks at: "); 55 | // Serial.println(millis()); 56 | } 57 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example16_Multitab/header.hpp: -------------------------------------------------------------------------------- 1 | //This is the place to declare every single function 2 | //and global variable that is going to be reused between cpp files. 3 | 4 | 5 | //We are going to use the TaskScheduler, but only the declarations part. 6 | //Remember to put customization macros before the #include: 7 | #define _TASK_SLEEP_ON_IDLE_RUN 8 | #include 9 | 10 | //Let the runner object be a global, single instance shared between object files. 11 | extern Scheduler runner; 12 | extern Task t2; //the t2 is defined in file2, but we need to access it from file1. 13 | 14 | //This function needs to be shared (between file2 and file1). 15 | void t3Callback(); 16 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example16_Multitab/ts.cpp: -------------------------------------------------------------------------------- 1 | //This is the only .cpp file that gets the #include. 2 | //Without it, the linker would not find necessary TaskScheduler's compiled code. 3 | // 4 | //Remember to put customization macros here as well. 5 | // 6 | //And don't import any common headers (here: header.hpp) 7 | // 8 | //Really. This file needs to be short. All stuff is in TaskScheduler.h. 9 | 10 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 11 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 12 | // #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 13 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 14 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 15 | // #define _TASK_PRIORITY // Support for layered scheduling priority 16 | // #define _TASK_MICRO_RES // Support for microsecond resolution 17 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) 18 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 19 | 20 | #include 21 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example17_Timeout/Scheduler_example17_Timeout.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This eaxmple illustrates the use of overall Task timeout functionality: 3 | 4 | Task 1 - runs every 1 seconds and times out in 10 seconds 5 | Task 2 - runs every 5 seconds and resets the timeout every run, so runs continuosly even though the timeout is set to 10 seconds 6 | */ 7 | 8 | 9 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 10 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 11 | //#define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 12 | // #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 13 | // #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 14 | // #define _TASK_PRIORITY // Support for layered scheduling priority 15 | // #define _TASK_MICRO_RES // Support for microsecond resolution 16 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 ONLY) 17 | // #define _TASK_DEBUG // Make all methods and variables public for debug purposes 18 | // #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 19 | #define _TASK_TIMEOUT 20 | 21 | #include 22 | 23 | Scheduler ts; 24 | 25 | void task1Callback(); 26 | void task1OnDisable(); 27 | void task2Callback(); 28 | void task2OnDisable(); 29 | 30 | Task t1(1 * TASK_SECOND, TASK_FOREVER, &task1Callback, &ts, false, NULL, &task1OnDisable); 31 | Task t2(5 * TASK_SECOND, TASK_FOREVER, &task2Callback, &ts, false, NULL, &task2OnDisable); 32 | 33 | void setup() { 34 | // put your setup code here, to run once: 35 | Serial.begin(115200); 36 | 37 | Serial.println("TaskScheduler Timeout example"); 38 | Serial.println("============================="); 39 | 40 | t1.setTimeout(10 * TASK_SECOND); 41 | t2.setTimeout(10 * TASK_SECOND); 42 | 43 | ts.enableAll(); 44 | } 45 | 46 | void loop() { 47 | // put your main code here, to run repeatedly: 48 | ts.execute(); 49 | } 50 | 51 | 52 | void task1Callback() { 53 | Serial.print("Task 1:\t"); 54 | Serial.print(millis()); 55 | Serial.print(": t/out="); 56 | Serial.print(t1.getTimeout()); 57 | Serial.print("\tms until t/out="); 58 | Serial.println( t1.untilTimeout()); 59 | 60 | } 61 | void task1OnDisable() { 62 | if (t1.timedOut()) { 63 | Serial.println("Task 1 has timed out. Restarting"); 64 | t1.setInterval(1 * TASK_SECOND); 65 | t1.setIterations(15); 66 | t1.setTimeout(TASK_NOTIMEOUT); 67 | t1.enable(); 68 | } 69 | else { 70 | Serial.println("Task 1 has been disabled"); 71 | } 72 | } 73 | 74 | void task2Callback() { 75 | Serial.print("Task 2:\t"); 76 | Serial.print(millis()); 77 | Serial.print(": t/out="); 78 | Serial.print(t2.getTimeout()); 79 | Serial.print("\tms until t/out="); 80 | Serial.println( t2.untilTimeout()); 81 | t2.resetTimeout(); 82 | } 83 | void task2OnDisable() { 84 | if (t2.timedOut()) { 85 | Serial.println("Task 2 has timed out"); 86 | } 87 | else { 88 | Serial.println("Task 2 has been disabled"); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example18_StatusRequest_LTS_WDT_Timeout/Scheduler_example18_StatusRequest_LTS_WDT_Timeout.ino: -------------------------------------------------------------------------------- 1 | /** 2 | This is example 5 rewritten with Timeout, LTS and WDT functioanlity: 3 | - 1 second timeout is set for the main calculation task 4 | - LTS is used to address individual array elements for each sensor sinlce the callback code is shared 5 | - WDT is used to set the Task ID and use that as an index for array of distances (alternative to LTS) 6 | 7 | Original description: 8 | ==================== 9 | This test emulates querying 3 sensors once every 10 seconds, each could respond with a different delay 10 | (ultrasonic sensors for instance) and printing a min value of the three when all three have reported their values. 11 | The overall timeout of 1 second is setup as well. 12 | An error message needs to be printed if a timeout occurred instead of a value. 13 | 14 | Example5: 15 | Sketch uses 6066 bytes (18%) of program storage space. Maximum is 32256 bytes. 16 | Global variables use 1039 bytes (50%) of dynamic memory, leaving 1009 bytes for local variables. Maximum is 2048 bytes. 17 | Example 18: 18 | Sketch uses 5142 bytes (15%) of program storage space. Maximum is 32256 bytes. 19 | Global variables use 878 bytes (42%) of dynamic memory, leaving 1170 bytes for local variables. Maximum is 2048 bytes. 20 | */ 21 | 22 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 23 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 24 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 25 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 26 | #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 27 | #define _TASK_PRIORITY // Support for layered scheduling priority 28 | // #define _TASK_MICRO_RES // Support for microsecond resolution 29 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) 30 | #define _TASK_DEBUG // Make all methods and variables public for debug purposes 31 | #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 32 | #define _TASK_TIMEOUT // Support for overall task timeout 33 | 34 | #include 35 | 36 | StatusRequest measure; 37 | 38 | Scheduler ts, hts; 39 | 40 | // Callback methods prototypes 41 | void CycleCallback(); 42 | void CalcCallback(); 43 | bool CalcEnable(); 44 | void CalcDisable(); 45 | void SCallback(); bool SEnable(); 46 | 47 | // Tasks 48 | Task tSensor1(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 1 49 | Task tSensor2(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 2 50 | Task tSensor3(0, TASK_ONCE, &SCallback, &ts, false, &SEnable); // task ID = 3 51 | 52 | Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts); 53 | Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable); 54 | 55 | #define NO_OF_SENSORS 3 56 | long distance, d[NO_OF_SENSORS + 1], d_lts[NO_OF_SENSORS]; // d[] will be populated via task ID used as array indexes, d_lts will be addressed via LTS pointers 57 | 58 | void CycleCallback() { 59 | Serial.println(); 60 | Serial.print(millis()); Serial.print(":\t"); 61 | Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); 62 | 63 | distance = 0; 64 | measure.setWaiting(NO_OF_SENSORS); // Set the StatusRequest to wait for 3 signals. 65 | tCalculate.waitFor(&measure); 66 | } 67 | 68 | bool CalcEnable() { 69 | Serial.print(millis()); Serial.print(":\t"); 70 | Serial.println("CalcEnable: OnEnable"); 71 | Serial.println("Activating sensors and setting timeout"); 72 | 73 | tSensor1.restartDelayed(); 74 | tSensor2.restartDelayed(); 75 | tSensor3.restartDelayed(); 76 | 77 | return true; 78 | } 79 | 80 | void CalcDisable() { 81 | if (tCalculate.timedOut()) { 82 | measure.signalComplete(-1); // signal error 83 | Serial.print(millis()); Serial.print(":\t"); 84 | Serial.println("MeasureCallback: ***** Timeout *****"); 85 | // tSensor1.disable(); 86 | // tSensor2.disable(); 87 | // tSensor3.disable(); 88 | } 89 | } 90 | 91 | 92 | void CalcCallback() { 93 | Serial.print(millis()); Serial.print(":\t"); 94 | Serial.println("CalcCallback: calculating"); 95 | distance = -1; 96 | if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully 97 | distance = d[1] < d[2] ? d[1] : d[2]; 98 | distance = d[3] < distance ? d[3] : distance; 99 | Serial.print("CalcCallback: Min distance="); Serial.println(distance); 100 | Serial.println(); 101 | } 102 | } 103 | 104 | 105 | /** Simulation code for all sensors 106 | ------------------------------- 107 | */ 108 | bool SEnable() { 109 | Task &t = ts.currentTask(); 110 | int i = t.getId(); 111 | 112 | Serial.print(millis()); Serial.print(":\t"); 113 | Serial.print("SEnable: TaskID="); 114 | Serial.println(i); 115 | Serial.print("Triggering sensor. Delay="); 116 | 117 | t.setInterval( random(1200) ); // Simulating sensor delay, which could go over 1 second and cause timeout 118 | // One way to update the 3 distances with one codebase - use task id as an index 119 | d[i] = 0; 120 | 121 | // Another way to update the 3 distances with one codebase - use LTS pointers 122 | int *pd = (int*) t.getLtsPointer(); 123 | *pd = 0; 124 | 125 | Serial.println( t.getInterval() ); 126 | return true; 127 | } 128 | 129 | void SCallback() { 130 | Task &t = ts.currentTask(); 131 | int i = t.getId(); 132 | 133 | Serial.print(millis()); Serial.print(":\t"); 134 | Serial.print("SCallback: TaskID="); 135 | Serial.println(i); 136 | Serial.print("Emulating measurement. d="); 137 | 138 | d[i] = random(501); // pick a value from 0 to 500 "centimeters" simulating a measurement 139 | int *pd = (int*) t.getLtsPointer(); 140 | *pd = d[i]; 141 | 142 | measure.signal(); 143 | 144 | Serial.print(d[i]); 145 | Serial.print("\t"); 146 | Serial.println(*pd); 147 | } 148 | 149 | /** Main Arduino code 150 | Not much is left here - everything is taken care of by the framework 151 | */ 152 | void setup() { 153 | 154 | Serial.begin(115200); 155 | Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); 156 | randomSeed(analogRead(A0) + millis()); 157 | 158 | tSensor1.setLtsPointer(&d_lts[0]); 159 | tSensor2.setLtsPointer(&d_lts[1]); 160 | tSensor3.setLtsPointer(&d_lts[2]); 161 | 162 | ts.setHighPriorityScheduler(&hts); 163 | 164 | tCalculate.setTimeout(1 * TASK_SECOND); 165 | tCycle.enable(); 166 | } 167 | 168 | void loop() { 169 | 170 | ts.execute(); 171 | 172 | } 173 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object.ino: -------------------------------------------------------------------------------- 1 | /** 2 | This is example 5 rewritten with Timeout, LTS, WDT functioanlity + multitab and extra classes 3 | - 1 second timeout is set for the main calculation task 4 | - LTS is used to address task-specific sensor class object 5 | - WDT is used to set the Task ID and use that for identifying the tasks (debug) 6 | 7 | Original description: 8 | ==================== 9 | This test emulates querying 1 to 10 sensors once every 10 seconds, each could respond with a different delay 10 | (ultrasonic sensors for instance) and printing a max value of them when all have reported their values. 11 | The overall timeout of 1 second is setup as well. 12 | An error message needs to be printed if a timeout occurred instead of a distance value. 13 | 14 | Task and SuperSensor objects are dynamically created and destroyed as needed every 10 seconds 15 | */ 16 | 17 | // #define _TASK_TIMECRITICAL // Enable monitoring scheduling overruns 18 | #define _TASK_SLEEP_ON_IDLE_RUN // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass 19 | #define _TASK_STATUS_REQUEST // Compile with support for StatusRequest functionality - triggering tasks on status change events in addition to time only 20 | #define _TASK_WDT_IDS // Compile with support for wdt control points and task ids 21 | #define _TASK_LTS_POINTER // Compile with support for local task storage pointer 22 | #define _TASK_PRIORITY // Support for layered scheduling priority 23 | // #define _TASK_MICRO_RES // Support for microsecond resolution 24 | // #define _TASK_STD_FUNCTION // Support for std::function (ESP8266 and ESP32 ONLY) 25 | #define _TASK_DEBUG // Make all methods and variables public for debug purposes 26 | #define _TASK_INLINE // Make all methods "inline" - needed to support some multi-tab, multi-file implementations 27 | #define _TASK_TIMEOUT // Support for overall task timeout 28 | 29 | #include 30 | #include "SuperSensor.h" 31 | 32 | StatusRequest measure; 33 | 34 | Scheduler ts, hts; 35 | 36 | // Callback methods prototypes 37 | void CycleCallback(); 38 | void CalcCallback(); 39 | bool CalcEnable(); 40 | void CalcDisable(); 41 | void SCallback(); 42 | bool SEnable(); 43 | void SDisable(); 44 | 45 | // Tasks 46 | 47 | Task tCycle(10000, TASK_FOREVER, &CycleCallback, &hts); 48 | Task tCalculate(TASK_IMMEDIATE , TASK_ONCE, &CalcCallback, &hts, false, &CalcEnable, &CalcDisable); 49 | 50 | int numberSensors; 51 | long distance; 52 | int pins[] = { 1, 9, 3, 7, 5, 6, 4, 8, 2, 10 }; 53 | 54 | void CycleCallback() { 55 | Serial.println();Serial.println();Serial.println(); 56 | Serial.print(millis()); Serial.print(":\t"); 57 | Serial.println("CycleCallback: Initiating measurement cycle every 10 seconds"); 58 | Serial.print("Number of sensors="); 59 | 60 | numberSensors = random(1, 11); // 1 to 10 sensors, randomly 61 | distance = 0; 62 | Serial.println(numberSensors); 63 | 64 | measure.setWaiting(numberSensors); // Set the StatusRequest to wait for 3 signals. 65 | tCalculate.waitFor(&measure); 66 | tCalculate.setTimeout(1000 * TASK_MILLISECOND); 67 | } 68 | 69 | bool CalcEnable() { 70 | Serial.print(millis()); Serial.print(":\t"); 71 | Serial.println("CalcEnable: OnEnable"); 72 | Serial.println("Activating sensors"); 73 | 74 | 75 | for (int i = 0; i < numberSensors; i++) { 76 | Task *t = new Task(TASK_MILLISECOND, TASK_FOREVER, &SCallback, &ts, false, &SEnable, &SDisable); 77 | SuperSensor *s = new SuperSensor( pins[i] ); 78 | t->setLtsPointer( (void*) s); 79 | t->setId(i + 1); 80 | 81 | s->begin(); 82 | 83 | t->restartDelayed(); 84 | } 85 | 86 | return true; 87 | } 88 | 89 | void CalcDisable() { 90 | if (tCalculate.timedOut()) { 91 | measure.signalComplete(-1); // signal error 92 | Serial.print(millis()); Serial.print(":\t"); 93 | Serial.println("MeasureCallback: ***** Timeout *****"); 94 | } 95 | ts.disableAll(false); // only disable tasks in the ts scheduler 96 | } 97 | 98 | 99 | void CalcCallback() { 100 | Serial.print(millis()); Serial.print(":\t"); 101 | Serial.println("CalcCallback: calculating"); 102 | if ( measure.getStatus() >= 0) { // only calculate if statusrequest ended successfully 103 | Serial.print("CalcCallback: Max distance="); Serial.println(distance); 104 | Serial.println(); 105 | } 106 | } 107 | 108 | 109 | /** Simulation code for all sensors 110 | ------------------------------- 111 | */ 112 | bool SEnable() { 113 | Task &t = ts.currentTask(); 114 | int i = t.getId(); 115 | 116 | Serial.print(millis()); Serial.print(":\t"); 117 | Serial.print("SEnable: TaskID="); 118 | Serial.println(i); 119 | Serial.print("Triggering sensor. Delay="); 120 | 121 | 122 | // Another way to update the distances with one codebase - use LTS pointers 123 | SuperSensor *s = (SuperSensor*) t.getLtsPointer(); 124 | 125 | long dly = s->trigger(); 126 | 127 | 128 | Serial.println( dly ); 129 | return true; 130 | } 131 | 132 | void SCallback() { 133 | Task &t = ts.currentTask(); 134 | 135 | SuperSensor *s = (SuperSensor*) t.getLtsPointer(); 136 | if ( s->measurementReady() ) { 137 | int i = t.getId(); 138 | Serial.print(millis()); Serial.print(":\t"); 139 | Serial.print("SCallback: TaskID="); 140 | Serial.println(i); 141 | Serial.print("Emulating measurement. d="); 142 | 143 | long d = s->value(); 144 | if ( d > distance ) distance = d; 145 | 146 | Serial.println(d); 147 | 148 | measure.signal(); 149 | t.disable(); 150 | } 151 | } 152 | 153 | void SDisable() { 154 | Task &t = ts.currentTask(); 155 | int i = t.getId(); 156 | 157 | Serial.print(millis()); Serial.print(":\t"); 158 | Serial.print("SDisable: TaskID="); 159 | Serial.println(i); 160 | 161 | SuperSensor *s = (SuperSensor*) ts.currentLts(); 162 | s->stop(); 163 | 164 | delete s; 165 | delete &t; 166 | } 167 | 168 | /** Main Arduino code 169 | Not much is left here - everything is taken care of by the framework 170 | */ 171 | void setup() { 172 | 173 | Serial.begin(115200); 174 | Serial.println("TaskScheduler StatusRequest Sensor Emulation Test. Complex Test."); 175 | randomSeed(analogRead(A0) + millis()); 176 | 177 | ts.setHighPriorityScheduler(&hts); 178 | 179 | tCalculate.setTimeout(1 * TASK_SECOND); 180 | tCycle.enable(); 181 | } 182 | 183 | void loop() { 184 | ts.execute(); 185 | } 186 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.cpp: -------------------------------------------------------------------------------- 1 | #include "SuperSensor.h" 2 | 3 | SuperSensor::SuperSensor(int aPin) { 4 | iPin = aPin; 5 | } 6 | 7 | SuperSensor::~SuperSensor() { 8 | iValue = -1; 9 | } 10 | 11 | void SuperSensor::begin() { 12 | iDelay = random(300, 1500); 13 | iValue = -1; 14 | } 15 | 16 | void SuperSensor::stop() { 17 | //nothing to do 18 | } 19 | 20 | long SuperSensor::trigger() { 21 | iStart = millis(); 22 | return iDelay; 23 | } 24 | 25 | bool SuperSensor::measurementReady() { 26 | if ( millis() - iStart > iDelay ) { 27 | iValue = random(501); 28 | return true; 29 | } 30 | return false; 31 | } 32 | 33 | long SuperSensor::value() { 34 | return iValue; 35 | } 36 | 37 | -------------------------------------------------------------------------------- /lib/TaskScheduler/examples/Scheduler_example20_StatusRequest_LTS_WDT_Timeout_Object/SuperSensor.h: -------------------------------------------------------------------------------- 1 | #ifndef _SUPER_SENSOR_H 2 | #define _SUPER_SENSOR_H 3 | 4 | 5 | #include "Arduino.h" 6 | #include 7 | 8 | class SuperSensor { 9 | public: 10 | SuperSensor(int aPin); 11 | ~SuperSensor(); 12 | void begin(); 13 | void stop(); 14 | long trigger(); 15 | bool measurementReady(); 16 | long value(); 17 | 18 | private: 19 | long iDelay; 20 | long iValue; 21 | int iPin; 22 | unsigned long iStart; 23 | }; 24 | 25 | #endif // _SUPER_SENSOR_H 26 | -------------------------------------------------------------------------------- /lib/TaskScheduler/extras/TaskScheduler.doc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NUKnightLab/SensorGrid/33ce8d1358c6cdaa241d6065f5a627406d6265fe/lib/TaskScheduler/extras/TaskScheduler.doc -------------------------------------------------------------------------------- /lib/TaskScheduler/extras/TaskScheduler.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NUKnightLab/SensorGrid/33ce8d1358c6cdaa241d6065f5a627406d6265fe/lib/TaskScheduler/extras/TaskScheduler.pdf -------------------------------------------------------------------------------- /lib/TaskScheduler/extras/TaskScheduler_html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NUKnightLab/SensorGrid/33ce8d1358c6cdaa241d6065f5a627406d6265fe/lib/TaskScheduler/extras/TaskScheduler_html.png -------------------------------------------------------------------------------- /lib/TaskScheduler/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For TaskManager 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | Task KEYWORD1 10 | Scheduler KEYWORD1 11 | StatusRequest KEYWORD1 12 | 13 | ####################################### 14 | # Methods and Functions (KEYWORD2) 15 | ####################################### 16 | 17 | init KEYWORD2 18 | addTask KEYWORD2 19 | deleteTask KEYWORD2 20 | disableAll KEYWORD2 21 | enableAll KEYWORD2 22 | currentTask KEYWORD2 23 | currentLts KEYWORD2 24 | execute KEYWORD2 25 | timeUntilNextIteration KEYWORD2 26 | startNow KEYWORD2 27 | allowSleep KEYWORD2 28 | enable KEYWORD2 29 | enableIfNot KEYWORD2 30 | enableDelayed KEYWORD2 31 | delay KEYWORD2 32 | forceNextIteration KEYWORD2 33 | restart KEYWORD2 34 | restartDelayed KEYWORD2 35 | disable KEYWORD2 36 | isEnabled KEYWORD2 37 | set KEYWORD2 38 | setInterval KEYWORD2 39 | getInterval KEYWORD2 40 | setIterations KEYWORD2 41 | getIterations KEYWORD2 42 | getRunCounter KEYWORD2 43 | setCallback KEYWORD2 44 | setOnEnable KEYWORD2 45 | setOnDisable KEYWORD2 46 | disableOnLastIteration KEYWORD2 47 | yield KEYWORD2 48 | yieldOnce KEYWORD2 49 | getInternalStatusRequest KEYWORD2 50 | getCount KEYWORD2 51 | getOverrun KEYWORD2 52 | getStartDelay KEYWORD2 53 | isFirstIteration KEYWORD2 54 | isLastIteration KEYWORD2 55 | setWaiting KEYWORD2 56 | signal KEYWORD2 57 | signalComplete KEYWORD2 58 | pending KEYWORD2 59 | completed KEYWORD2 60 | getStatus KEYWORD2 61 | waitFor KEYWORD2 62 | waitForDelayed KEYWORD2 63 | getStatusRequest KEYWORD2 64 | getCount KEYWORD2 65 | setId KEYWORD2 66 | getId KEYWORD2 67 | setControlPoint KEYWORD2 68 | getControlPoint KEYWORD2 69 | setLtsPointer KEYWORD2 70 | getLtsPointer KEYWORD2 71 | isOverrun KEYWORD2 72 | setHighPriorityScheduler KEYWORD2 73 | currentScheduler KEYWORD2 74 | setTimeout KEYWORD2 75 | resetTimeout KEYWORD2 76 | getTimeout KEYWORD2 77 | untilTimeout KEYWORD2 78 | timedOut KEYWORD2 79 | ####################################### 80 | # Constants (LITERAL1) 81 | TASK_MILLISECOND LITERAL1 82 | TASK_SECOND LITERAL1 83 | TASK_MINUTE LITERAL1 84 | TASK_HOUR LITERAL1 85 | TASK_FOREVER LITERAL1 86 | TASK_IMMEDIATE LITERAL1 87 | TASK_ONCE LITERAL1 88 | TASK_NOTIMEOUT LITERAL1 89 | _TASK_TIMECRITICAL LITERAL1 90 | _TASK_SLEEP_ON_IDLE_RUN LITERAL1 91 | _TASK_STATUS_REQUEST LITERAL1 92 | _TASK_WDT_IDS LITERAL1 93 | _TASK_LTS_POINTER LITERAL1 94 | _TASK_PRIORITY LITERAL1 95 | _TASK_MICRO_RES LITERAL1 96 | _TASK_STD_FUNCTION LITERAL1 97 | _TASK_DEBUG LITERAL1 98 | _TASK_INLINE LITERAL1 99 | _TASK_TIMEOUT LITERAL1 100 | TaskCallback LITERAL1 101 | TaskOnDisable LITERAL1 102 | TaskOnEnable LITERAL1 103 | ####################################### 104 | 105 | -------------------------------------------------------------------------------- /lib/TaskScheduler/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TaskScheduler", 3 | "keywords": "multitasking, cooperative, event, task, taskscheduler, scheduling", 4 | "description": "Cooperative multitasking for Arduino and ESP8266 microcontrollers", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/arkhipenko/TaskScheduler" 9 | }, 10 | "authors": 11 | [ 12 | { 13 | "name": "Anatoli Arkhipenko", 14 | "email": "arkhipenko@hotmail.com", 15 | "url": "https://github.com/arkhipenko", 16 | "maintainer": true 17 | } 18 | ], 19 | "version": "2.6.1", 20 | "frameworks": "arduino", 21 | "platforms": "*" 22 | } 23 | -------------------------------------------------------------------------------- /lib/TaskScheduler/library.properties: -------------------------------------------------------------------------------- 1 | name=TaskScheduler 2 | version=2.6.1 3 | author=Anatoli Arkhipenko 4 | maintainer=Anatoli Arkhipenko 5 | sentence=A light-weight cooperative multitasking library for arduino and esp8266 microcontrollers. 6 | paragraph=Supports: periodic task execution (with dynamic execution period in milliseconds or microseconds – frequency of execution), number of iterations (limited or infinite number of iterations), execution of tasks in predefined sequence, dynamic change of task execution parameters (frequency, number of iterations, callback methods), power saving via entering IDLE sleep mode when tasks are not scheduled to run, event-driven task invocation via Status Request object, task IDs and Control Points for error handling and watchdog timer, Local Task Storage pointer (allowing use of same callback code for multiple tasks), layered task prioritization, std::functions (esp8266, esp32 only), overall task timeout. 7 | category=Timing 8 | url=https://github.com/arkhipenko/TaskScheduler.git 9 | architectures=* 10 | -------------------------------------------------------------------------------- /lib/readme.txt: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for the project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link to executable file. 4 | 5 | The source code of each library should be placed in separate directory, like 6 | "lib/private_lib/[here are source files]". 7 | 8 | For example, see how can be organized `Foo` and `Bar` libraries: 9 | 10 | |--lib 11 | | |--Bar 12 | | | |--docs 13 | | | |--examples 14 | | | |--src 15 | | | |- Bar.c 16 | | | |- Bar.h 17 | | |--Foo 18 | | | |- Foo.c 19 | | | |- Foo.h 20 | | |- readme.txt --> THIS FILE 21 | |- platformio.ini 22 | |--src 23 | |- main.c 24 | 25 | Then in `src/main.c` you should use: 26 | 27 | #include 28 | #include 29 | 30 | // rest H/C/CPP code 31 | 32 | PlatformIO will find your libraries automatically, configure preprocessor's 33 | include paths and build them. 34 | 35 | More information about PlatformIO Library Dependency Finder 36 | - http://docs.platformio.org/page/librarymanager/ldf.html 37 | -------------------------------------------------------------------------------- /platformio.ini.example: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; http://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | ;src_dir = SensorGridPM 13 | src_dir = SensorGridLoRaCollector 14 | 15 | [env:adafruit_feather_m0] 16 | platform = atmelsam 17 | monitor_speed = 115200 18 | board = adafruit_feather_m0 19 | framework = arduino 20 | lib_deps = 21 | RTCZero@1.5.2 22 | RTClib@1.2.1 23 | SdFat@1.0.5 24 | ArduinoJson@6.11.5 25 | Adafruit SSD1306@1.1.2 26 | Adafruit GFX Library@1.2.3 27 | Adafruit Feather OLED@1 28 | Adafruit Si7021 Library@1.2.0 29 | WiFi101 30 | Wire 31 | Adafruit GPS Library 32 | lib_extra_dirs = 33 | lib 34 | ~/Documents/Arduino/libraries 35 | -------------------------------------------------------------------------------- /resources/README.md: -------------------------------------------------------------------------------- 1 | https://github.com/sandeepmistry/arduino-LoRa 2 | 3 | https://github.com/sandeepmistry/arduino-LoRa/issues/90 4 | 5 | https://www.airspayce.com/mikem/arduino/RadioHead/index.html 6 | 7 | http://static1.squarespace.com/static/54cecce7e4b054df1848b5f9/t/57489e6e07eaa0105215dc6c/1464376943218/Reversing-Lora-Knight.pdf 8 | 9 | https://www.semtech.com/uploads/documents/LoraDesignGuide_STD.pdf 10 | 11 | https://www.link-labs.com/blog/lora-faqs 12 | 13 | https://www.semtech.com/uploads/documents/fcc_part15_regulations_semtech.pdf 14 | 15 | https://revspace.nl/DecodingLora#CRC 16 | 17 | https://cdn-shop.adafruit.com/product-files/3179/sx1276_77_78_79.pdf 18 | 19 | https://cdn-learn.adafruit.com/assets/assets/000/031/659/original/RFM95_96_97_98W.pdf?1460518717 20 | 21 | https://www.hoperf.com/modules/lora/RFM95.html 22 | 23 | https://arxiv.org/pdf/1712.02141.pdf 24 | -------------------------------------------------------------------------------- /resources/RadioHead.1.8.2.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NUKnightLab/SensorGrid/33ce8d1358c6cdaa241d6065f5a627406d6265fe/resources/RadioHead.1.8.2.tar.gz --------------------------------------------------------------------------------