Luis Rodil-Fernandez
2 years ago
34 changed files with 1655 additions and 0 deletions
-
20lib/LICENSE
-
6lib/README.md
-
20lib/corestats/LICENSE
-
6lib/corestats/README.md
-
188lib/corestats/corestats.cpp
-
180lib/corestats/corestats.h
-
24lib/corestats/examples/normalize/normalize.ino
-
23lib/corestats/examples/runningavg/runningavg.ino
-
39lib/corestats/keywords.txt
-
1lib/derfunke/README.md
-
15lib/derfunke/example/header.cpp
-
12lib/derfunke/src/derfunke.cpp
-
5lib/derfunke/src/derfunke.h
-
4lib/easywifi/README.md
-
24lib/easywifi/example/client.cpp
-
146lib/easywifi/src/easywifi.cpp
-
34lib/easywifi/src/easywifi.h
-
3lib/nanolog/README.md
-
194lib/nanolog/src/nanolog.h
-
17lib/ota/README.md
-
38lib/ota/example/otatest.cpp
-
51lib/ota/src/ota.cpp
-
7lib/ota/src/ota.h
-
5lib/smoothie/README.md
-
25lib/smoothie/example/compare.cpp
-
6lib/smoothie/src/smoothie.cpp
-
44lib/smoothie/src/smoothie.h
-
6lib/ugen/README.md
-
35lib/ugen/examples/duoplot.cpp
-
338lib/ugen/src/ugen.cpp
-
57lib/ugen/src/ugen.h
-
2platformio.ini
-
66src/main.cpp
-
14src/settings.h
@ -0,0 +1,20 @@ |
|||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) 2013-2021 Luis Rodil-Fernandez |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||
|
this software and associated documentation files (the "Software"), to deal in |
||||
|
the Software without restriction, including without limitation the rights to |
||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
||||
|
the Software, and to permit persons to whom the Software is furnished to do so, |
||||
|
subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,6 @@ |
|||||
|
# DERFUNKE McUKIT |
||||
|
|
||||
|
This is a collection of libraries for common needs when programming Arduino's or ESP microcontrollers. Meant for own use and for use in teaching. |
||||
|
|
||||
|
Run `git submodule init` on this directory to fetch the libraries that are not part of the unirepo. |
||||
|
|
@ -0,0 +1,20 @@ |
|||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) 2013 Luis Rodil-Fernandez |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of |
||||
|
this software and associated documentation files (the "Software"), to deal in |
||||
|
the Software without restriction, including without limitation the rights to |
||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
||||
|
the Software, and to permit persons to whom the Software is furnished to do so, |
||||
|
subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in all |
||||
|
copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,6 @@ |
|||||
|
## Core Analog Statistics |
||||
|
|
||||
|
A support library for analog time series analysis. |
||||
|
|
||||
|
Code mostly borrowed from [Plaquette by Sofian Audrey](https://github.com/SofaPirate/Plaquette). |
||||
|
|
@ -0,0 +1,188 @@ |
|||||
|
#include <corestats.h>
|
||||
|
|
||||
|
#include <Arduino.h>
|
||||
|
#include <limits.h>
|
||||
|
#include <math.h>
|
||||
|
#include <float.h>
|
||||
|
|
||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||
|
|
||||
|
float Stats::stddev() const { |
||||
|
return sqrt(var()); |
||||
|
} |
||||
|
|
||||
|
float Stats::normalize(float value) const { |
||||
|
return ( value - mean() ) / (stddev() + FLT_MIN); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
SimpleStats::SimpleStats(float startMean, float startVar) { |
||||
|
reset(startMean, startVar); |
||||
|
} |
||||
|
|
||||
|
void SimpleStats::reset(float startMean, float startVar) { |
||||
|
_mean = startMean; |
||||
|
_mean2 = startVar + sq(_mean); |
||||
|
_nSamples = 0; |
||||
|
// _min = FLT_MAX;
|
||||
|
// _max = -FLT_MAX;
|
||||
|
} |
||||
|
|
||||
|
float SimpleStats::update(float value) { |
||||
|
if (_nSamples == ULONG_MAX) |
||||
|
_nSamples = (ULONG_MAX / 4) * 3; // simple trick that makes sure we don't overflow
|
||||
|
|
||||
|
// Add one to number of samples
|
||||
|
_nSamples++; |
||||
|
|
||||
|
// Update mean and mean2
|
||||
|
float prop = (float)(_nSamples-1) / (float)_nSamples; |
||||
|
_mean = _mean * prop + value / _nSamples; |
||||
|
_mean2 = _mean2 * prop + sq(value) / _nSamples; |
||||
|
|
||||
|
// Update min and max
|
||||
|
// _min = min(_min, value);
|
||||
|
// _max = max(_max, value);
|
||||
|
|
||||
|
return normalize(value); |
||||
|
} |
||||
|
|
||||
|
float SimpleStats::var() const { |
||||
|
float v = (_mean2 - sq(_mean)); |
||||
|
return max(v, (float)0); // make sure the result is >= 0
|
||||
|
} |
||||
|
|
||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||
|
|
||||
|
MovingAverage::MovingAverage(float alphaOrN) : _value(0.5f) { |
||||
|
setAlphaOrN(alphaOrN); |
||||
|
reset(); |
||||
|
} |
||||
|
|
||||
|
MovingAverage::MovingAverage(float alphaOrN, float startValue) : _value(startValue) { |
||||
|
setAlphaOrN(alphaOrN); |
||||
|
} |
||||
|
|
||||
|
void MovingAverage::setAlphaOrN(float alphaOrN) |
||||
|
{ |
||||
|
alphaOrN = max(alphaOrN, 0); // make sure factor >= 0
|
||||
|
_alpha = (alphaOrN > 1 ? |
||||
|
2 / (alphaOrN + 1) : |
||||
|
alphaOrN); |
||||
|
} |
||||
|
|
||||
|
void MovingAverage::reset() { |
||||
|
_setStarted(false); |
||||
|
} |
||||
|
|
||||
|
void MovingAverage::reset(float startValue) { |
||||
|
_value = startValue; |
||||
|
_setStarted(true); |
||||
|
} |
||||
|
|
||||
|
float MovingAverage::update(float v) { |
||||
|
if (!isStarted()) { |
||||
|
_value = v; |
||||
|
_setStarted(true); // start
|
||||
|
return _value; |
||||
|
} |
||||
|
else |
||||
|
return (_value -= _alpha * (_value - v)); |
||||
|
} |
||||
|
|
||||
|
bool MovingAverage::isStarted() const { |
||||
|
return _alpha >= 0; |
||||
|
} |
||||
|
|
||||
|
void MovingAverage::_setStarted(bool start) { |
||||
|
_alpha = (start ? +1 : -1) * abs(_alpha); |
||||
|
} |
||||
|
|
||||
|
MovingStats::MovingStats(float alphaOrN) : avg(alphaOrN) { } |
||||
|
MovingStats::MovingStats(float alphaOrN, float startMean, float startVar) |
||||
|
: avg(alphaOrN, startMean), _var(startVar) { |
||||
|
reset(startMean, startVar); |
||||
|
} |
||||
|
|
||||
|
void MovingStats::reset() { |
||||
|
avg.reset(); |
||||
|
_var = 0; |
||||
|
} |
||||
|
|
||||
|
void MovingStats::reset(float startMean, float startVar) { |
||||
|
avg.reset(startMean); |
||||
|
_var = startVar; |
||||
|
} |
||||
|
|
||||
|
float MovingStats::update(float value) |
||||
|
{ |
||||
|
avg.update(value); |
||||
|
if (!isStarted()) |
||||
|
_var = 0; |
||||
|
else { |
||||
|
float diff = value - avg.get(); |
||||
|
_var -= avg.alpha() * (_var - sq(diff)); |
||||
|
} |
||||
|
|
||||
|
return normalize(value); |
||||
|
} |
||||
|
|
||||
|
bool MovingStats::isStarted() const { |
||||
|
return avg.isStarted(); |
||||
|
} |
||||
|
|
||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||
|
// //////////////////////////////////////////////////////////////////////////
|
||||
|
|
||||
|
AdaptiveNormalizer::AdaptiveNormalizer(float smoothFactor) |
||||
|
: MovingStats(smoothFactor), |
||||
|
_value(0.5f), |
||||
|
_mean(0.5f), |
||||
|
_stddev(0.25f) |
||||
|
{} |
||||
|
|
||||
|
AdaptiveNormalizer::AdaptiveNormalizer(float mean, float stddev, float smoothFactor) |
||||
|
: MovingStats(smoothFactor), |
||||
|
_value(mean), |
||||
|
_mean(mean), |
||||
|
_stddev(abs(stddev)) |
||||
|
{} |
||||
|
|
||||
|
float AdaptiveNormalizer::put(float value) { |
||||
|
return (_value = MovingStats::update(value) * _stddev + _mean); |
||||
|
} |
||||
|
|
||||
|
Normalizer::Normalizer() |
||||
|
: SimpleStats(), |
||||
|
_value(0.5f), |
||||
|
_mean(0.5f), |
||||
|
_stddev(0.25f) |
||||
|
{} |
||||
|
|
||||
|
Normalizer::Normalizer(float mean, float stddev) |
||||
|
: SimpleStats(), |
||||
|
_value(mean), |
||||
|
_mean(mean), |
||||
|
_stddev(abs(stddev)) |
||||
|
{} |
||||
|
|
||||
|
float Normalizer::put(float value) { |
||||
|
return (_value = SimpleStats::update(value) * _stddev + _mean); |
||||
|
} |
||||
|
|
||||
|
MinMaxScaler::MinMaxScaler() |
||||
|
: _value(0.5f), |
||||
|
_minValue(FLT_MAX), |
||||
|
_maxValue(FLT_MIN) |
||||
|
{} |
||||
|
|
||||
|
float MinMaxScaler::put(float value) |
||||
|
{ |
||||
|
_minValue = min(value, _minValue); |
||||
|
_maxValue = max(value, _maxValue); |
||||
|
_value = (_minValue == _maxValue ? 0.5f : map(value, _minValue, _maxValue, 0.0f, 1.0f)); |
||||
|
return _value; |
||||
|
} |
||||
|
|
@ -0,0 +1,180 @@ |
|||||
|
#ifndef __CORESTATS_H__ |
||||
|
#define __CORESTATS_H__ |
||||
|
|
||||
|
class Stats { |
||||
|
public: |
||||
|
virtual ~Stats() {} |
||||
|
|
||||
|
/// Resets the statistics. |
||||
|
virtual void reset(float startMean=0, float startVar=0) = 0; |
||||
|
|
||||
|
/// Adds a value to the statistics (returns the mean). |
||||
|
virtual float update(float value) = 0; |
||||
|
|
||||
|
/// The statistics. |
||||
|
virtual float mean() const = 0; |
||||
|
virtual float var() const = 0; |
||||
|
virtual float stddev() const; |
||||
|
|
||||
|
/// Returns the normalized value according to the computed statistics (mean and variance). |
||||
|
float normalize(float value) const; |
||||
|
}; |
||||
|
|
||||
|
class SimpleStats : public Stats { |
||||
|
public: |
||||
|
float _mean; |
||||
|
float _mean2; // mean of squared values |
||||
|
|
||||
|
unsigned long _nSamples; |
||||
|
|
||||
|
/// ctor |
||||
|
SimpleStats(float startMean=0, float startVar=0); |
||||
|
virtual ~SimpleStats() {} |
||||
|
|
||||
|
/// Resets the statistics. |
||||
|
virtual void reset(float startMean=0, float startVar=0); |
||||
|
|
||||
|
/// Adds a value to the statistics (returns the mean). |
||||
|
virtual float update(float value); |
||||
|
|
||||
|
/// The statistics. |
||||
|
virtual float mean() const { return _mean; } |
||||
|
// The var() and stddev() are the population (ie. not the sample) variance and standard dev, so technically |
||||
|
// they should be readjusted by multiplying it by _nSamples / (_nSamples-1). But with a lot of samples the |
||||
|
// difference vanishes and we priviledged less floating points computations over precision. |
||||
|
virtual float var() const; |
||||
|
}; |
||||
|
|
||||
|
/// An exponential moving average class. |
||||
|
class MovingAverage { |
||||
|
public: |
||||
|
// The alpha (mixing) variable (in [0,1]). |
||||
|
float _alpha; |
||||
|
|
||||
|
// The current value of the exponential moving average. |
||||
|
float _value; |
||||
|
|
||||
|
/** |
||||
|
* Constructs the moving average, starting with #startValue# as its value. The #alphaOrN# argument |
||||
|
* has two options: |
||||
|
* - if <= 1 then it's used directly as the alpha value |
||||
|
* - if > 1 then it's used as the "number of items that are considered from the past" (*) |
||||
|
* (*) Of course this is an approximation. It actually sets the alpha value to 2 / (n - 1) |
||||
|
*/ |
||||
|
MovingAverage(float alphaOrN=1); |
||||
|
MovingAverage(float alphaOrN, float startValue); |
||||
|
virtual ~MovingAverage() {} |
||||
|
|
||||
|
/// Change the smoothing factor to #alphaOrN#. |
||||
|
void setAlphaOrN(float alphaOrN); |
||||
|
|
||||
|
/// Resets the moving average. |
||||
|
void reset(); |
||||
|
|
||||
|
/// Resets the moving average to #startValue#. |
||||
|
void reset(float startValue); |
||||
|
|
||||
|
/// Updates the moving average with new value #v# (also returns the current value). |
||||
|
float update(float v); |
||||
|
|
||||
|
/// Returns the value of the moving average. This is undefined if isValid() == false. |
||||
|
float get() const { return _value; } |
||||
|
|
||||
|
/// Returns true iff the moving average has already been started. |
||||
|
bool isStarted() const; |
||||
|
|
||||
|
/// Returns the alpha value. |
||||
|
float alpha() const { return _alpha; } |
||||
|
|
||||
|
protected: |
||||
|
void _setStarted(bool start); |
||||
|
}; |
||||
|
|
||||
|
class MovingStats : public Stats { |
||||
|
public: |
||||
|
MovingAverage avg; |
||||
|
float _var; |
||||
|
|
||||
|
/** |
||||
|
* Constructs the moving statistics, starting with #startMean# and #startVar# as initial mean and |
||||
|
* variance. The #alphaOrN# argument has two options: |
||||
|
* - if <= 1 then it's used directly as the alpha value |
||||
|
* - if > 1 then it's used as the "number of items that are considered from the past" (*) |
||||
|
* (*) Of course this is an approximation. It actually sets the alpha value to 2 / (n - 1) |
||||
|
*/ |
||||
|
MovingStats(float alphaOrN=1); |
||||
|
MovingStats(float alphaOrN, float startMean, float startVar); |
||||
|
virtual ~MovingStats() {} |
||||
|
|
||||
|
/// Resets the statistics. |
||||
|
virtual void reset(); |
||||
|
|
||||
|
/// Resets the statistics. |
||||
|
virtual void reset(float startMean, float startVar); |
||||
|
|
||||
|
/// Adds a value to the statistics (returns the mean). |
||||
|
virtual float update(float value); |
||||
|
|
||||
|
/// The statistics. |
||||
|
virtual float mean() const { return avg.get(); } |
||||
|
virtual float var() const { return _var; } |
||||
|
|
||||
|
virtual bool isStarted() const; |
||||
|
}; |
||||
|
|
||||
|
|
||||
|
/// Adaptive normalizer: normalizes values on-the-run using exponential moving |
||||
|
/// averages over mean and stddev. |
||||
|
class AdaptiveNormalizer : public MovingStats { |
||||
|
public: |
||||
|
AdaptiveNormalizer(float smoothFactor=0.001f); |
||||
|
AdaptiveNormalizer(float mean, float stddev, float smoothFactor=0.001f); |
||||
|
virtual ~AdaptiveNormalizer() {} |
||||
|
|
||||
|
void setMean(float mean) { _mean = mean; } |
||||
|
void setStddev(float stddev) { _stddev = stddev; }; |
||||
|
|
||||
|
virtual float put(float value); |
||||
|
|
||||
|
virtual float get() { return _value; } |
||||
|
|
||||
|
float _value; |
||||
|
float _mean; |
||||
|
float _stddev; |
||||
|
}; |
||||
|
|
||||
|
/// Standard normalizer: normalizes values on-the-run using real mean and stddev. |
||||
|
class Normalizer : public SimpleStats { |
||||
|
public: |
||||
|
Normalizer(); |
||||
|
Normalizer(float mean, float stddev); |
||||
|
virtual ~Normalizer() {} |
||||
|
|
||||
|
void setMean(float mean) { _mean = mean; } |
||||
|
void setStddev(float stddev) { _stddev = stddev; }; |
||||
|
|
||||
|
virtual float put(float value); |
||||
|
|
||||
|
virtual float get() { return _value; } |
||||
|
|
||||
|
float _value; |
||||
|
float _mean; |
||||
|
float _stddev; |
||||
|
}; |
||||
|
|
||||
|
/// Regularizes signal into [0,1] by rescaling it using the min and max values. |
||||
|
class MinMaxScaler { |
||||
|
public: |
||||
|
MinMaxScaler(); |
||||
|
virtual ~MinMaxScaler() {} |
||||
|
|
||||
|
virtual float put(float value); |
||||
|
|
||||
|
virtual float get() { return _value; } |
||||
|
|
||||
|
float _value; |
||||
|
float _minValue; |
||||
|
float _maxValue; |
||||
|
}; |
||||
|
|
||||
|
#endif // __CORESTATS_H__ |
@ -0,0 +1,24 @@ |
|||||
|
#include <corestats.h>
|
||||
|
|
||||
|
#define SENSOR_PIN A0
|
||||
|
|
||||
|
// normalize incoming signal to be inside range [0, 1]
|
||||
|
AdaptiveNormalizer normalizer(0, 1); |
||||
|
|
||||
|
void setup() { |
||||
|
Serial.begin(115200); |
||||
|
} |
||||
|
|
||||
|
void loop() { |
||||
|
// process input.
|
||||
|
int reading = analogRead(SENSOR_PIN); |
||||
|
|
||||
|
normalizer.put( reading ); |
||||
|
|
||||
|
Serial.print(reading); |
||||
|
Serial.print(","); |
||||
|
Serial.print( normalizer.get() ); |
||||
|
Serial.println(); |
||||
|
|
||||
|
delay(20); |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
#include <corestats.h>
|
||||
|
|
||||
|
#define SENSOR_PIN A0
|
||||
|
|
||||
|
MovingAverage mavg(0.01); |
||||
|
|
||||
|
void setup() { |
||||
|
Serial.begin(115200); |
||||
|
} |
||||
|
|
||||
|
void loop() { |
||||
|
// process input.
|
||||
|
int reading = analogRead(SENSOR_PIN); |
||||
|
|
||||
|
mavg.update( reading ); |
||||
|
|
||||
|
Serial.print(reading); |
||||
|
Serial.print(","); |
||||
|
Serial.print( mavg.get() ); |
||||
|
Serial.println(); |
||||
|
|
||||
|
delay(20); |
||||
|
} |
@ -0,0 +1,39 @@ |
|||||
|
####################################### |
||||
|
# Syntax Coloring Map For Test |
||||
|
####################################### |
||||
|
|
||||
|
####################################### |
||||
|
# Datatypes (KEYWORD1) |
||||
|
####################################### |
||||
|
|
||||
|
Stats KEYWORD1 |
||||
|
SimpleStats KEYWORD1 |
||||
|
MovingAverage KEYWORD1 |
||||
|
MovingStats KEYWORD1 |
||||
|
AdaptiveNormalizer KEYWORD1 |
||||
|
Normalizer KEYWORD1 |
||||
|
MinMaxScaler KEYWORD1 |
||||
|
|
||||
|
####################################### |
||||
|
# Methods and Functions (KEYWORD2) |
||||
|
####################################### |
||||
|
|
||||
|
put KEYWORD2 |
||||
|
get KEYWORD2 |
||||
|
mean KEYWORD2 |
||||
|
var KEYWORD2 |
||||
|
stddev KEYWORD2 |
||||
|
normalize KEYWORD2 |
||||
|
reset KEYWORD2 |
||||
|
update KEYWORD2 |
||||
|
alpha KEYWORD2 |
||||
|
isStarted KEYWORD2 |
||||
|
|
||||
|
####################################### |
||||
|
# Instances (KEYWORD2) |
||||
|
####################################### |
||||
|
|
||||
|
####################################### |
||||
|
# Constants (LITERAL1) |
||||
|
####################################### |
||||
|
|
@ -0,0 +1 @@ |
|||||
|
## Vanity files |
@ -0,0 +1,15 @@ |
|||||
|
#include <derfunke.h>
|
||||
|
|
||||
|
void setup() |
||||
|
{ |
||||
|
Serial.begin(115200); |
||||
|
// welcome serial output header
|
||||
|
df_header_print(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void loop() |
||||
|
{ |
||||
|
delay(500); |
||||
|
} |
||||
|
|
@ -0,0 +1,12 @@ |
|||||
|
#include <Arduino.h>
|
||||
|
#include "derfunke.h"
|
||||
|
|
||||
|
void df_header_print() { |
||||
|
Serial.println(" __ ___ __ "); |
||||
|
Serial.println(" ___/ /__ ____/ _/_ _____ / /_____ "); |
||||
|
Serial.println("/ _ / -_) __/ _/ // / _ \\/ '_/ -_)"); |
||||
|
Serial.println("\\_,_/\\__/_/ /_/ \\_,_/_//_/_/\\_\\__/ "); |
||||
|
Serial.print (" M C U K I T "); |
||||
|
Serial.println(MCUKIT_RELEASE); |
||||
|
Serial.println(); |
||||
|
} |
@ -0,0 +1,5 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#define MCUKIT_RELEASE "r12" |
||||
|
|
||||
|
void df_header_print(); |
@ -0,0 +1,4 @@ |
|||||
|
# Easy Wifi |
||||
|
|
||||
|
Wraps around Wifi connectivity to make it easy for students to connect their devices to wifi |
||||
|
|
@ -0,0 +1,24 @@ |
|||||
|
#include <Arduino.h>
|
||||
|
#include <easywifi.h>
|
||||
|
|
||||
|
#define WIFI_SSID "somessid"
|
||||
|
#define WIFI_PASSWORD "somepassword"
|
||||
|
|
||||
|
void setup() { |
||||
|
Serial.begin( 9600 ); |
||||
|
while(!Serial); // wait until the serial connection is stablished
|
||||
|
|
||||
|
// connect to existing access point as client
|
||||
|
if( wifi_connect_as_client(WIFI_SSID, WIFI_PASSWORD) ) { |
||||
|
// print debugging information
|
||||
|
wifi_print_mode(); |
||||
|
wifi_print_ip(); |
||||
|
} else { |
||||
|
Serial.print("Failed to connect to wifi "); |
||||
|
Serial.print( WIFI_SSID ); |
||||
|
Serial.println(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void loop() { |
||||
|
} |
@ -0,0 +1,146 @@ |
|||||
|
#define LOG_ENABLED
|
||||
|
|
||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||
|
#include <WiFi.h>
|
||||
|
#else
|
||||
|
#include <ESP8266WiFi.h>
|
||||
|
#endif
|
||||
|
|
||||
|
#include <Arduino.h>
|
||||
|
#include <WiFiUdp.h>
|
||||
|
#include "easywifi.h"
|
||||
|
#include <nanolog.h>
|
||||
|
|
||||
|
// IPAddress thisip;
|
||||
|
// WifiMode mode;
|
||||
|
// String apssid = "default_ap";
|
||||
|
// String _ssid = "unknown", _password = "unknown";
|
||||
|
|
||||
|
tWifiSettings wifi; |
||||
|
|
||||
|
// default network settings when operating as an AP
|
||||
|
IPAddress ip_static = IPAddress(10, 0, 0, 1); |
||||
|
IPAddress gw_static = IPAddress(10, 0, 0, 1); |
||||
|
IPAddress sn_static = IPAddress(255, 255, 255, 0); |
||||
|
|
||||
|
void wifi_set_credentials(const char *ssid, const char *passw) { |
||||
|
wifi.ssid = ssid; |
||||
|
wifi.password = passw; |
||||
|
} |
||||
|
|
||||
|
void wifi_set_default_ap_name(const char *name) { |
||||
|
wifi.apssid = name; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* connect to WiFi access point as client station |
||||
|
* |
||||
|
* @param ssid to connect to |
||||
|
* @param passw password |
||||
|
* @param timeout is in seconds |
||||
|
*/ |
||||
|
bool wifi_connect_as_client(const char *ssid, const char *passw, int timeouts) { |
||||
|
boolean succeeded = true; |
||||
|
|
||||
|
wifi_set_credentials(ssid, passw); |
||||
|
|
||||
|
WiFi.mode(WIFI_STA); // both hotspot and client are enabled
|
||||
|
WiFi.begin(ssid, passw); |
||||
|
LOG("Connecting to WiFi "); |
||||
|
LOG(ssid); |
||||
|
LOG_NEW_LINE |
||||
|
|
||||
|
// wait for connection
|
||||
|
LOG("Connecting"); |
||||
|
unsigned long tout = millis() + (timeouts * 1000); |
||||
|
while (WiFi.status() != WL_CONNECTED) { |
||||
|
delay(200); |
||||
|
Serial.print("."); |
||||
|
if (millis() > tout) { |
||||
|
succeeded = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
LOG_NEW_LINE |
||||
|
|
||||
|
// if we are connected we probably were assigned an IP
|
||||
|
if (succeeded) { |
||||
|
wifi.thisip = WiFi.localIP(); |
||||
|
} |
||||
|
|
||||
|
wifi.mode = WifiMode::CLIENT; |
||||
|
|
||||
|
return succeeded; |
||||
|
} |
||||
|
|
||||
|
bool wifi_create_ap(const char *myssid) { |
||||
|
WiFi.mode(WIFI_AP); |
||||
|
|
||||
|
// optional soft ip config
|
||||
|
LOG("Custom AP IP/GW/Subnet"); |
||||
|
LOG_NEW_LINE |
||||
|
WiFi.softAPConfig(ip_static, gw_static, sn_static); |
||||
|
|
||||
|
WiFi.softAP(myssid); |
||||
|
LOG("Creating WiFi access point with name "); |
||||
|
LOG(myssid); |
||||
|
LOG_NEW_LINE |
||||
|
|
||||
|
// let a long delay happen here, otherwise we might not get an IP
|
||||
|
delay(1000); |
||||
|
|
||||
|
// // setup the DNS server redirecting all the domains to the ap IP
|
||||
|
// dnsServer.setErrorReplyCode(DNSReplyCode::NoError);
|
||||
|
// dnsServer.start(DNS_PORT, "*", WiFi.softAPIP());
|
||||
|
|
||||
|
wifi.thisip = WiFi.softAPIP(); |
||||
|
|
||||
|
wifi.apssid = myssid; |
||||
|
wifi.mode = WifiMode::ACCESS_POINT; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
void wifi_print_ip() { |
||||
|
LOG("IP address: "); |
||||
|
LOG( wifi.thisip ); |
||||
|
} |
||||
|
|
||||
|
void wifi_print_mode() { |
||||
|
if( WifiMode::CLIENT == wifi.mode ) { |
||||
|
LOG("Connected to "); |
||||
|
LOG(wifi.ssid); |
||||
|
LOG(" as client"); |
||||
|
LOG_NEW_LINE |
||||
|
} else if (WifiMode::ACCESS_POINT == wifi.mode) { |
||||
|
LOG("Created "); |
||||
|
LOG(wifi.apssid); |
||||
|
LOG(" access point"); |
||||
|
LOG_NEW_LINE |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void wifi_init() |
||||
|
{ |
||||
|
// first try to connect to a given WiFi AP
|
||||
|
if( wifi_connect_as_client( wifi.ssid.c_str(), wifi.password.c_str()) ) { |
||||
|
// // save IP
|
||||
|
// persistence_save_settings();
|
||||
|
|
||||
|
// // create mDNS entry
|
||||
|
// String mdns = "pyropanda" + thisip[3];
|
||||
|
// network_create_mdns_entry( mdns.c_str() );
|
||||
|
} else { |
||||
|
// if we do not succeed at connecting to an existing AP, create our own
|
||||
|
// and setup DNS to channel all connections to the captive portal
|
||||
|
LOG("Connection failed."); |
||||
|
LOG_NEW_LINE |
||||
|
|
||||
|
wifi_create_ap( wifi.apssid.c_str() ); |
||||
|
} |
||||
|
|
||||
|
// always print debug info
|
||||
|
wifi_print_mode(); |
||||
|
wifi_print_ip(); |
||||
|
LOG_NEW_LINE |
||||
|
} // wifi_init
|
@ -0,0 +1,34 @@ |
|||||
|
#ifndef __EASYWIFI_H__ |
||||
|
#define __EASYWIFI_H__ |
||||
|
|
||||
|
#include <nanolog.h> |
||||
|
|
||||
|
#ifndef __NANOLOG_H__ |
||||
|
#warning "the easywifi library needs the nanolog library as well" |
||||
|
#endif |
||||
|
|
||||
|
enum WifiMode { |
||||
|
CLIENT = 10, // station node |
||||
|
HYBRID_AP = 15, // station + ap |
||||
|
ACCESS_POINT = 20, // ap only |
||||
|
MESH = 30 // mesh |
||||
|
}; |
||||
|
|
||||
|
struct tWifiSettings { |
||||
|
IPAddress thisip; |
||||
|
WifiMode mode; |
||||
|
String apssid; |
||||
|
String ssid, password; |
||||
|
}; |
||||
|
|
||||
|
extern tWifiSettings wifi; |
||||
|
|
||||
|
void wifi_init(); |
||||
|
void wifi_set_credentials(const char *ssid, const char *passw); |
||||
|
void wifi_set_default_ap_name(const char *ssid); |
||||
|
void wifi_print_ip(); |
||||
|
void wifi_print_mode(); |
||||
|
bool wifi_connect_as_client(const char *ssid, const char *passw, int timeouts = 10); |
||||
|
bool wifi_create_ap(const char *myssid); |
||||
|
|
||||
|
#endif // __EASYWIFI_H__ |
@ -0,0 +1,3 @@ |
|||||
|
# Nanolog |
||||
|
|
||||
|
Simple serial logging library for microcontrollers |
@ -0,0 +1,194 @@ |
|||||
|
#pragma once |
||||
|
#ifndef __NANOLOG_H__ |
||||
|
#define __NANOLOG_H__ |
||||
|
|
||||
|
#include "Arduino.h" |
||||
|
|
||||
|
// #define LOG(severity, msg) p msg; |
||||
|
|
||||
|
// void p(const char *fmt, ... ) |
||||
|
// { |
||||
|
// static char tmp[128]; // resulting string limited to 128 chars. |
||||
|
// va_list args; |
||||
|
// va_start (args, fmt ); |
||||
|
// vsnprintf(tmp, 128, fmt, args); |
||||
|
// va_end (args); |
||||
|
// Serial.println(tmp); |
||||
|
// delay(1); |
||||
|
// } |
||||
|
|
||||
|
inline void LOG_SPACE_IMPL(); |
||||
|
inline void LOG_SET_IMPL(int8_t i); |
||||
|
inline void LOG_SET_IMPL(int16_t i); |
||||
|
inline void LOG_SET_IMPL(int32_t i); |
||||
|
inline void LOG_SET_IMPL(int64_t i); |
||||
|
inline void LOG_SET_IMPL(uint8_t i); |
||||
|
inline void LOG_SET_IMPL(uint16_t i); |
||||
|
inline void LOG_SET_IMPL(uint32_t i); |
||||
|
inline void LOG_SET_IMPL(uint64_t i); |
||||
|
inline void LOG_SET_IMPL(char c); |
||||
|
inline void LOG_SET_IMPL(String s); |
||||
|
inline void LOG_SET_IMPL(float f); |
||||
|
inline void LOG_SET_IMPL(double d); |
||||
|
inline void LOG_NEW_LINE_IMPL(); |
||||
|
|
||||
|
#ifdef LOG_ENABLED |
||||
|
|
||||
|
#define LOG(P) LOG_SET_IMPL(P) |
||||
|
#define LOG_NEW_LINE LOG_NEW_LINE_IMPL(); |
||||
|
#define LOG_SPACE LOG_SPACE_IMPL() |
||||
|
|
||||
|
#else |
||||
|
|
||||
|
#define LOG(P) |
||||
|
#define LOG_NEW_LINE |
||||
|
#define LOG_SPACE |
||||
|
|
||||
|
#endif |
||||
|
|
||||
|
|
||||
|
inline void LOG_INIT() |
||||
|
{ |
||||
|
#if defined (LOG_ENABLED) |
||||
|
Serial.begin(115200); |
||||
|
LOG("Initialized: SERIAL_LOG64"); |
||||
|
LOG_NEW_LINE; |
||||
|
#endif |
||||
|
|
||||
|
#if defined (EEPROM_LOG_ENABLED) |
||||
|
LOG_EEPROM_READ; |
||||
|
|
||||
|
EEPROM_LOG_ADDR_PTR = EEPROM_LOG_Start_Address; |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SPACE_IMPL() |
||||
|
{ |
||||
|
Serial.print(" "); |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(int8_t i) |
||||
|
{ |
||||
|
Serial.print(i); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(int16_t i) |
||||
|
{ |
||||
|
Serial.print(i); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(int32_t i) |
||||
|
{ |
||||
|
Serial.print(i); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(uint8_t i) |
||||
|
{ |
||||
|
Serial.print(i); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(uint16_t i) |
||||
|
{ |
||||
|
Serial.print(i); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(uint32_t i) |
||||
|
{ |
||||
|
Serial.print(i); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline void LOG_SET_IMPL(char c) |
||||
|
{ |
||||
|
Serial.print(c); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
inline void LOG_SET_IMPL(String s) |
||||
|
{ |
||||
|
Serial.print(s); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(float f) |
||||
|
{ |
||||
|
Serial.print(f); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(double d) |
||||
|
{ |
||||
|
Serial.print(d); |
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_NEW_LINE_IMPL() |
||||
|
{ |
||||
|
Serial.println(" "); |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(uint64_t n) |
||||
|
{ |
||||
|
unsigned char buf[16 * sizeof(long)]; |
||||
|
|
||||
|
if (n == 0) |
||||
|
{ |
||||
|
Serial.print((char)'0'); |
||||
|
LOG_SPACE; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
for (uint16_t i = 0; n > 0; i++) |
||||
|
{ |
||||
|
buf[i] = n % 10; |
||||
|
n /= 10; |
||||
|
} |
||||
|
|
||||
|
for (uint16_t i = 0; i > 0; i--) |
||||
|
{ |
||||
|
Serial.print((char) (buf[i - 1] < 10 ? ('0' + buf[i - 1]) : ('A' + buf[i - 1] - 10))); |
||||
|
} |
||||
|
|
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
inline void LOG_SET_IMPL(int64_t n) |
||||
|
{ |
||||
|
unsigned char buf[16 * sizeof(long)]; ; |
||||
|
|
||||
|
if (n == 0) |
||||
|
{ |
||||
|
Serial.print((char)'0'); |
||||
|
LOG_SPACE; |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (n < 0) |
||||
|
{ |
||||
|
Serial.print((char)'-'); |
||||
|
n = n * (-1); |
||||
|
} |
||||
|
|
||||
|
for (uint16_t i = 0; n > 0; i++) |
||||
|
{ |
||||
|
buf[i] = n % 10; |
||||
|
n /= 10; |
||||
|
} |
||||
|
|
||||
|
for (uint16_t i = 0; i > 0; i--) |
||||
|
{ |
||||
|
Serial.print((char) (buf[i - 1] < 10 ? ('0' + buf[i - 1]) : ('A' + buf[i - 1] - 10))); |
||||
|
} |
||||
|
|
||||
|
LOG_SPACE; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#endif // __NANOLOG_H__ |
@ -0,0 +1,17 @@ |
|||||
|
# Over the Air updates |
||||
|
|
||||
|
If you are on Platformio, remember that to use OTA you need to set the upload port and the protocol properly in the `platformio.ini` file. Like: |
||||
|
|
||||
|
``` |
||||
|
; for OTA stuff you have to update the upload_port |
||||
|
upload_protocol = espota |
||||
|
upload_port = IP_ADDRESS_HERE or mDNS_NAME.local |
||||
|
``` |
||||
|
|
||||
|
And remember that you can get serial debug output via telnet using the `TelnetStream` library. |
||||
|
|
||||
|
``` |
||||
|
lib_deps = |
||||
|
TelnetStream |
||||
|
``` |
||||
|
|
@ -0,0 +1,38 @@ |
|||||
|
#define LOG_ENABLED
|
||||
|
#include <Arduino.h>
|
||||
|
#include <derfunke.h>
|
||||
|
#include <easywifi.h>
|
||||
|
#include <ota.h>
|
||||
|
|
||||
|
// i like to put these two in another file, so that it's
|
||||
|
// not checked in git commits
|
||||
|
#define SSID "some_ssid"
|
||||
|
#define PASSWD "some_password"
|
||||
|
|
||||
|
void setup() { |
||||
|
Serial.begin(115200); |
||||
|
while(!Serial) ; |
||||
|
|
||||
|
// welcome serial output header
|
||||
|
df_header_print(); |
||||
|
|
||||
|
// we need to be connected to wifi for OTA to
|
||||
|
// initialize properly
|
||||
|
if( wifi_connect_as_client(SSID, PASSWD) ) { |
||||
|
wifi_print_mode(); |
||||
|
wifi_print_ip(); |
||||
|
} else { |
||||
|
Serial.print("Failed to connect to wifi "); |
||||
|
Serial.print( SSID ); |
||||
|
Serial.println(); |
||||
|
} |
||||
|
|
||||
|
// this is the name the device will take as an OTA port
|
||||
|
ota_init("racoon"); |
||||
|
} |
||||
|
|
||||
|
void loop() { |
||||
|
ota_loop(); |
||||
|
|
||||
|
delay(100); // chill for a bit
|
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
#include "ota.h"
|
||||
|
|
||||
|
void ota_init(const char *name = NULL) { |
||||
|
// Port defaults to 3232
|
||||
|
// ArduinoOTA.setPort(3232);
|
||||
|
|
||||
|
if(NULL != name) { |
||||
|
// Hostname defaults to esp3232-[MAC]
|
||||
|
ArduinoOTA.setHostname( name ); |
||||
|
} |
||||
|
|
||||
|
// No authentication by default
|
||||
|
// ArduinoOTA.setPassword("admin");
|
||||
|
|
||||
|
// Password can be set with it's md5 value as well
|
||||
|
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
|
||||
|
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
|
||||
|
|
||||
|
ArduinoOTA |
||||
|
.onStart([]() { |
||||
|
String type; |
||||
|
if (ArduinoOTA.getCommand() == U_FLASH) |
||||
|
type = "sketch"; |
||||
|
else // U_SPIFFS
|
||||
|
type = "filesystem"; |
||||
|
|
||||
|
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
|
||||
|
Serial.println("OTA update [" + type + "] begins"); |
||||
|
}) |
||||
|
.onEnd([]() { |
||||
|
Serial.println("\nOTA update ended"); |
||||
|
}) |
||||
|
.onProgress([](unsigned int progress, unsigned int total) { |
||||
|
Serial.printf("OTA update progress: %u%%\r", (progress / (total / 100))); |
||||
|
}) |
||||
|
.onError([](ota_error_t error) { |
||||
|
Serial.printf("OTA update error[%u]: ", error); |
||||
|
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); |
||||
|
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); |
||||
|
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); |
||||
|
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); |
||||
|
else if (error == OTA_END_ERROR) Serial.println("End Failed"); |
||||
|
}); |
||||
|
|
||||
|
ArduinoOTA.begin(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void ota_loop() { |
||||
|
ArduinoOTA.handle(); |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
#pragma once |
||||
|
|
||||
|
#include <ArduinoOTA.h> |
||||
|
#include <Update.h> |
||||
|
|
||||
|
void ota_init(const char *name); |
||||
|
void ota_loop(); |
@ -0,0 +1,5 @@ |
|||||
|
# Signal smoothing |
||||
|
|
||||
|
A simple library to smooth values from analog readings in microcontroller projects. |
||||
|
|
||||
|
|
@ -0,0 +1,25 @@ |
|||||
|
#include <Arduino.h>
|
||||
|
#include <smoothie.h>
|
||||
|
|
||||
|
Smoothie<int> reading(0.1); |
||||
|
|
||||
|
void setup() { |
||||
|
|
||||
|
Serial.begin( 9600 ); |
||||
|
while(!Serial); |
||||
|
|
||||
|
pinMode(A2, INPUT); |
||||
|
} |
||||
|
|
||||
|
void loop() { |
||||
|
int noisy = analogRead( A2 ); |
||||
|
int avg = analogReadAvg( A2 ); |
||||
|
int val = reading.filter( analogRead( A2 ) ); |
||||
|
|
||||
|
Serial.print( noisy ); |
||||
|
Serial.print(", "); |
||||
|
Serial.print( avg ); |
||||
|
Serial.print(", "); |
||||
|
Serial.print( val ); |
||||
|
Serial.println(); |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
/**
|
||||
|
* Signal smoothing utility functions |
||||
|
* |
||||
|
*/ |
||||
|
#include <Arduino.h>
|
||||
|
#include "Smoothie.h"
|
@ -0,0 +1,44 @@ |
|||||
|
#ifndef __SMOOTHIE_H__ |
||||
|
#define __SMOOTHIE_H__ |
||||
|
|
||||
|
/** |
||||
|
* Simple weighed moving average |
||||
|
* from https://makeabilitylab.github.io/physcomp/advancedio/smoothing-input.html |
||||
|
*/ |
||||
|
template<typename T> |
||||
|
class Smoothie { |
||||
|
|
||||
|
float sigma, s; |
||||
|
|
||||
|
public: |
||||
|
Smoothie(float _sigma, float _s = 0); |
||||
|
T filter(T val); |
||||
|
}; |
||||
|
|
||||
|
template<typename T> |
||||
|
Smoothie<T>::Smoothie(float _sigma, float _s) { |
||||
|
sigma = _sigma; |
||||
|
s = _sigma; |
||||
|
} |
||||
|
|
||||
|
template<typename T> |
||||
|
T Smoothie<T>::filter(T val) { |
||||
|
s = (sigma * val) + (1 - sigma) * s; |
||||
|
return s; |
||||
|
} |
||||
|
|
||||
|
/// inlined functions |
||||
|
|
||||
|
/// returns the average value of a few consecutive readings |
||||
|
inline int analogReadAvg(int pin, int iter = 4) { |
||||
|
int retval = 0; |
||||
|
|
||||
|
int i = iter; |
||||
|
while(i > 0) { |
||||
|
retval += analogRead( pin ); |
||||
|
} |
||||
|
|
||||
|
return retval / iter; |
||||
|
} // analogReadAvg |
||||
|
|
||||
|
#endif // __SMOOTHIE_H__ |
@ -0,0 +1,6 @@ |
|||||
|
# Function generators / Oscillators |
||||
|
|
||||
|
Original from Rob Tillart with some minor modifications to fit own personal use. |
||||
|
|
||||
|
Thanks to Rob for the great library! |
||||
|
|
@ -0,0 +1,35 @@ |
|||||
|
#include <ugen.h>
|
||||
|
|
||||
|
Ugen gen1; |
||||
|
Ugen gen2; |
||||
|
|
||||
|
|
||||
|
void setup() |
||||
|
{ |
||||
|
Serial.begin(115200); |
||||
|
|
||||
|
gen1.setFrequency(13); |
||||
|
gen1.setAmplitude(50); |
||||
|
gen1.setPhase(0); |
||||
|
gen1.setYShift(0); |
||||
|
|
||||
|
gen2.setFrequency(17); |
||||
|
gen2.setAmplitude(25); |
||||
|
gen2.setPhase(0.25); |
||||
|
gen2.setYShift(25); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void loop() |
||||
|
{ |
||||
|
float t = millis() * 0.001; |
||||
|
float x = gen1.sinus(t); |
||||
|
float y = gen2.sinus(t); |
||||
|
Serial.print(x); |
||||
|
Serial.print("\t"); |
||||
|
Serial.print(y); |
||||
|
Serial.print("\t"); |
||||
|
Serial.print(x + y); |
||||
|
Serial.println(); |
||||
|
} |
||||
|
|
@ -0,0 +1,338 @@ |
|||||
|
//
|
||||
|
// FILE: functionGenerator.cpp
|
||||
|
// AUTHOR: Rob Tillaart
|
||||
|
// VERSION: 0.2.2
|
||||
|
// PURPOSE: wave form generating functions (use with care)
|
||||
|
// URL: https://github.com/RobTillaart/FunctionGenerator
|
||||
|
//
|
||||
|
// HISTORY:
|
||||
|
// 0.1.00 2015-01-01 initial version
|
||||
|
// 0.1.01 2015-01-01 initial class version
|
||||
|
// 0.1.02 2015-01-01 refactor and research
|
||||
|
// 0.1.03 2015-01-02 added stair, more refactoring
|
||||
|
// 0.1.04 2015-01-03 added integer versions - to be used with 8 bit DAC
|
||||
|
// 0.1.5 2017-07-29 Fix issue #33 (dbl -> float)
|
||||
|
// 0.2.0 2020-06-10 main refactoring and cleanup
|
||||
|
// 0.2.1 2020-12-24 Arduino-CI + unit tests
|
||||
|
// 0.2.2 2021-11-02 update Arduino-CI, badges
|
||||
|
// add mode for sawtooth and stair.
|
||||
|
//
|
||||
|
|
||||
|
|
||||
|
#include "ugen.h"
|
||||
|
|
||||
|
|
||||
|
Ugen::Ugen(float period, float amplitude, float phase, float yShift) |
||||
|
{ |
||||
|
setPeriod(period); |
||||
|
setAmplitude(amplitude); |
||||
|
setPhase(phase); |
||||
|
setYShift(yShift); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
void Ugen::setPeriod(float period) |
||||
|
{ |
||||
|
_period = period; |
||||
|
_freq1 = 1 / period; |
||||
|
_freq2 = 2 * _freq1; |
||||
|
_freq4 = 4 * _freq1; |
||||
|
_freq0 = TWO_PI * _freq1; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float Ugen::line() |
||||
|
{ |
||||
|
return _yShift + _amplitude; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float Ugen::zero() |
||||
|
{ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float Ugen::sawtooth(float t, uint8_t mode) |
||||
|
{ |
||||
|
float rv; |
||||
|
t += _phase; |
||||
|
if (t >= 0.0) |
||||
|
{ |
||||
|
if (t >= _period) t = fmod(t, _period); |
||||
|
if (mode == 1) t = _period - t; |
||||
|
rv = _amplitude * (-1.0 + t *_freq2); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
t = -t; |
||||
|
if (t >= _period) t = fmod(t, _period); |
||||
|
if (mode == 1) t = _period - t; |
||||
|
rv = _amplitude * ( 1.0 - t * _freq2); |
||||
|
} |
||||
|
rv += _yShift; |
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float Ugen::triangle(float t) |
||||
|
{ |
||||
|
float rv; |
||||
|
t += _phase; |
||||
|
if (t < 0.0) |
||||
|
{ |
||||
|
t = -t; |
||||
|
} |
||||
|
if (t >= _period) t = fmod(t, _period); |
||||
|
if ( t * 2 < _period) |
||||
|
{ |
||||
|
rv = _amplitude * (-1.0 + t * _freq4); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
rv = _amplitude * (3.0 - t * _freq4); |
||||
|
} |
||||
|
rv += _yShift; |
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float Ugen::square(float t) |
||||
|
{ |
||||
|
float rv; |
||||
|
t += _phase; |
||||
|
if (t >= 0) |
||||
|
{ |
||||
|
if (t >= _period) t = fmod(t, _period); |
||||
|
if ((t + t) < _period) rv = _amplitude; |
||||
|
else rv = -_amplitude; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
t = -t; |
||||
|
if (t >= _period) t = fmod(t, _period); |
||||
|
if ( t * 2 < _period) rv = -_amplitude; |
||||
|
else rv = _amplitude; |
||||
|
} |
||||
|
rv += _yShift; |
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float Ugen::sinus(float t) |
||||
|
{ |
||||
|
float rv; |
||||
|
t += _phase; |
||||
|
rv = _amplitude * sin(t * _freq0); |
||||
|
rv += _yShift; |
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float Ugen::stair(float t, uint16_t steps, uint8_t mode) |
||||
|
{ |
||||
|
t += _phase; |
||||
|
if (t >= 0) |
||||
|
{ |
||||
|
if (t >= _period) t = fmod(t, _period); |
||||
|
if (mode == 1) t = _period - t; |
||||
|
int level = steps * t / _period; |
||||
|
return _yShift + _amplitude * (-1.0 + 2.0 * level / (steps - 1)); |
||||
|
} |
||||
|
t = -t; |
||||
|
if (t >= _period) t = fmod(t, _period); |
||||
|
if (mode == 1) t = _period - t; |
||||
|
int level = steps * t / _period; |
||||
|
return _yShift + _amplitude * (1.0 - 2.0 * level / (steps - 1)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float Ugen::random() |
||||
|
{ |
||||
|
// TODO smart reseed needed
|
||||
|
float rv = _yShift + _amplitude * _random() * 0.2328306436E-9; // div 0xFFFFFFFF
|
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// An example of a simple pseudo-random number generator is the
|
||||
|
// Multiply-with-carry method invented by George Marsaglia.
|
||||
|
// two initializers (not null)
|
||||
|
uint32_t Ugen::_random() |
||||
|
{ |
||||
|
_m_z = 36969L * (_m_z & 65535L) + (_m_z >> 16); |
||||
|
_m_w = 18000L * (_m_w & 65535L) + (_m_w >> 16); |
||||
|
return (_m_z << 16) + _m_w; /* 32-bit result */ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//
|
||||
|
// INTEGER VERSIONS FOR 8 BIT DAC
|
||||
|
//
|
||||
|
// 8 bits version
|
||||
|
// t = 0..9999 period 10000 in millis, returns 0..255
|
||||
|
|
||||
|
/*
|
||||
|
|
||||
|
uint8_t ifgsaw(uint16_t t, uint16_t period = 1000) |
||||
|
{ |
||||
|
return 255L * t / period; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint8_t ifgtri(uint16_t t, uint16_t period = 1000) |
||||
|
{ |
||||
|
if (t * 2 < period) return 510L * t / period; |
||||
|
return 255L - 510L * t / period; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint8_t ifgsqr(uint16_t t, uint16_t period = 1000) |
||||
|
{ |
||||
|
if (t * 2 < period) return 510L * t / period; |
||||
|
return 255L - 510L * t / period; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint8_t ifgsin(uint16_t t, uint16_t period = 1000) |
||||
|
{ |
||||
|
return sin(355L * t / period / 113); // LUT
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
uint8_t ifgstr(uint16_t t, uint16_t period = 1000, uint16_t steps = 8) |
||||
|
{ |
||||
|
int level = 1L * steps * t / period; |
||||
|
return 255L * level / (steps - 1); |
||||
|
} |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
//
|
||||
|
// SIMPLE float ONES
|
||||
|
//
|
||||
|
// t = 0..period
|
||||
|
// period = 0.001 ... 10000 ?
|
||||
|
/*
|
||||
|
|
||||
|
float fgsaw(float t, float period = 1.0) |
||||
|
{ |
||||
|
if (t >= 0) return -1.0 + 2 * t / period; |
||||
|
return 1.0 + 2 * t / period; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float fgtri(float t, float period = 1.0) |
||||
|
{ |
||||
|
if (t < 0) t = -t; |
||||
|
if (t * 2 < period) return -1.0 + 4 * t / period; |
||||
|
return 3.0 - 4 * t / period; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float fgsqr(float t, float period = 1.0) |
||||
|
{ |
||||
|
if (t >= 0) |
||||
|
{ |
||||
|
if ( 2 * t < period) return 1.0; |
||||
|
return -1.0; |
||||
|
} |
||||
|
t = -t; |
||||
|
if (2 * t < period) return -1.0; |
||||
|
return 1.0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float fgsin(float t, float period = 1.0) |
||||
|
{ |
||||
|
return sin(TWO_PI * t / period); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float fgstr(float t, float period = 1.0, uint16_t steps = 8) |
||||
|
{ |
||||
|
if (t >= 0) |
||||
|
{ |
||||
|
int level = steps * t / period; |
||||
|
return -1.0 + 2.0 * level / (steps - 1); |
||||
|
} |
||||
|
t = -t; |
||||
|
int level = steps * t / period; |
||||
|
return 1.0 - 2.0 * level / (steps - 1); |
||||
|
} |
||||
|
|
||||
|
*/ |
||||
|
|
||||
|
/*
|
||||
|
//
|
||||
|
// FULL floatS ONES
|
||||
|
//
|
||||
|
float fgsaw(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0) |
||||
|
{ |
||||
|
t += phase; |
||||
|
if (t >= 0) |
||||
|
{ |
||||
|
if (t >= period) t = fmod(t, period); |
||||
|
return yShift + amplitude * (-1.0 + 2 * t / period); |
||||
|
} |
||||
|
t = -t; |
||||
|
if (t >= period) t = fmod(t, period); |
||||
|
return yShift + amplitude * ( 1.0 - 2 * t / period); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float fgtri(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0, float dutyCycle = 0.50) |
||||
|
{ |
||||
|
t += phase; |
||||
|
if (t < 0) t = -t; |
||||
|
if (t >= period) t = fmod(t, period); |
||||
|
// 50 % dutyCycle = faster
|
||||
|
// if (t * 2 < period) return yShift + amplitude * (-1.0 + 4 * t / period);
|
||||
|
// return yShift + amplitude * (3.0 - 4 * t / period);
|
||||
|
if (t < dutyCycle * period) return yShift + amplitude * (-1.0 + 2 * t / (dutyCycle * period)); |
||||
|
// return yShift + amplitude * (-1.0 + 2 / (1 - dutyCycle) - 2 * t / ((1 - dutyCycle) * period));
|
||||
|
return yShift + amplitude * (-1.0 + 2 / (1 - dutyCycle) * ( 1 - t / period)); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float fgsqr(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0, float dutyCycle = 0.50) |
||||
|
{ |
||||
|
t += phase; |
||||
|
if (t >= 0) |
||||
|
{ |
||||
|
if (t >= period) t = fmod(t, period); |
||||
|
if (t < dutyCycle * period) return yShift + amplitude; |
||||
|
return yShift - amplitude; |
||||
|
} |
||||
|
t = -t; |
||||
|
if (t >= period) t = fmod(t, period); |
||||
|
if (t < dutyCycle * period) return yShift - amplitude; |
||||
|
return yShift + amplitude; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float fgsin(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0) |
||||
|
{ |
||||
|
t += phase; |
||||
|
float rv = yShift + amplitude * sin(TWO_PI * t / period); |
||||
|
return rv; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
float fgstr(float t, float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0, uint16_t steps = 8) |
||||
|
{ |
||||
|
t += phase; |
||||
|
if (t >= 0) |
||||
|
{ |
||||
|
if (t >= period) t = fmod(t, period); |
||||
|
int level = steps * t / period; |
||||
|
return yShift + amplitude * (-1.0 + 2.0 * level / (steps - 1)); |
||||
|
} |
||||
|
t = -t; |
||||
|
if (t >= period) t = fmod(t, period); |
||||
|
int level = steps * t / period; |
||||
|
return yShift + amplitude * (1.0 - 2.0 * level / (steps - 1)); |
||||
|
} |
||||
|
|
||||
|
*/ |
@ -0,0 +1,57 @@ |
|||||
|
#pragma once |
||||
|
// |
||||
|
// FILE: functionGenerator.h |
||||
|
// AUTHOR: Rob Tillaart |
||||
|
// VERSION: 0.2.2 |
||||
|
// PURPOSE: wave form generating functions (use with care) |
||||
|
// URL: https://github.com/RobTillaart/FunctionGenerator |
||||
|
// |
||||
|
|
||||
|
|
||||
|
#include "Arduino.h" |
||||
|
|
||||
|
class Ugen |
||||
|
{ |
||||
|
public: |
||||
|
Ugen(float period = 1.0, float amplitude = 1.0, float phase = 0.0, float yShift = 0.0); |
||||
|
|
||||
|
// configuration |
||||
|
void setPeriod(float period = 1.0); |
||||
|
float getPeriod() { return _period; }; |
||||
|
void setFrequency(float freq = 1.0) { setPeriod(1/freq); }; |
||||
|
float getFrequency() { return _freq1; }; |
||||
|
|
||||
|
void setAmplitude(float ampl = 1.0) { _amplitude = ampl; }; |
||||
|
float getAmplitude() { return _amplitude; }; |
||||
|
void setPhase(float phase = 0.0) { _phase = phase; }; |
||||
|
float getPhase() { return _phase; }; |
||||
|
void setYShift(float yShift = 0.0) { _yShift = yShift; }; |
||||
|
float getYShift() { return _yShift; }; |
||||
|
|
||||
|
// constant amplitude |
||||
|
float line(); |
||||
|
// constant zero for calibration |
||||
|
float zero(); |
||||
|
|
||||
|
// standard wave forms |
||||
|
float sawtooth(float t, uint8_t mode = 0); |
||||
|
float triangle(float t); |
||||
|
float square(float t); |
||||
|
float sinus(float t); |
||||
|
float stair(float t, uint16_t steps = 8, uint8_t mode = 0); |
||||
|
float random(); |
||||
|
|
||||
|
private: |
||||
|
float _period; |
||||
|
float _freq0; |
||||
|
float _freq1; |
||||
|
float _freq2; |
||||
|
float _freq4; |
||||
|
float _amplitude; |
||||
|
float _phase; |
||||
|
float _yShift; |
||||
|
// Marsaglia 'constants' |
||||
|
uint32_t _m_w = 1; |
||||
|
uint32_t _m_z = 2; |
||||
|
uint32_t _random(); |
||||
|
}; |
@ -0,0 +1,14 @@ |
|||||
|
#ifndef __SETTINGS_H__ |
||||
|
#define __SETTINGS_H__ |
||||
|
|
||||
|
// wifi credentials |
||||
|
#define WIFI_SSID "" |
||||
|
#define WIFI_PASSWORD "" |
||||
|
|
||||
|
//#define MQTT_DEVICE_ID "" |
||||
|
#define MQTT_BROKER_USER "" |
||||
|
#define MQTT_BROKER_PASS "" |
||||
|
#define MQTT_BROKER "" |
||||
|
#define MQTT_PORT "" |
||||
|
|
||||
|
#endif // __SETTINGS_H__ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue