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