Browse Source

added mqtt support (untested)

master
Luis Rodil-Fernandez 2 years ago
parent
commit
94afab128f
  1. 20
      lib/LICENSE
  2. 6
      lib/README.md
  3. 20
      lib/corestats/LICENSE
  4. 6
      lib/corestats/README.md
  5. 188
      lib/corestats/corestats.cpp
  6. 180
      lib/corestats/corestats.h
  7. 24
      lib/corestats/examples/normalize/normalize.ino
  8. 23
      lib/corestats/examples/runningavg/runningavg.ino
  9. 39
      lib/corestats/keywords.txt
  10. 1
      lib/derfunke/README.md
  11. 15
      lib/derfunke/example/header.cpp
  12. 12
      lib/derfunke/src/derfunke.cpp
  13. 5
      lib/derfunke/src/derfunke.h
  14. 4
      lib/easywifi/README.md
  15. 24
      lib/easywifi/example/client.cpp
  16. 146
      lib/easywifi/src/easywifi.cpp
  17. 34
      lib/easywifi/src/easywifi.h
  18. 3
      lib/nanolog/README.md
  19. 194
      lib/nanolog/src/nanolog.h
  20. 17
      lib/ota/README.md
  21. 38
      lib/ota/example/otatest.cpp
  22. 51
      lib/ota/src/ota.cpp
  23. 7
      lib/ota/src/ota.h
  24. 5
      lib/smoothie/README.md
  25. 25
      lib/smoothie/example/compare.cpp
  26. 6
      lib/smoothie/src/smoothie.cpp
  27. 44
      lib/smoothie/src/smoothie.h
  28. 6
      lib/ugen/README.md
  29. 35
      lib/ugen/examples/duoplot.cpp
  30. 338
      lib/ugen/src/ugen.cpp
  31. 57
      lib/ugen/src/ugen.h
  32. 2
      platformio.ini
  33. 66
      src/main.cpp
  34. 14
      src/settings.h

20
lib/LICENSE

@ -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.

6
lib/README.md

@ -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.

20
lib/corestats/LICENSE

@ -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.

6
lib/corestats/README.md

@ -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).

188
lib/corestats/corestats.cpp

@ -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;
}

180
lib/corestats/corestats.h

@ -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__

24
lib/corestats/examples/normalize/normalize.ino

@ -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);
}

23
lib/corestats/examples/runningavg/runningavg.ino

@ -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);
}

39
lib/corestats/keywords.txt

@ -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)
#######################################

1
lib/derfunke/README.md

@ -0,0 +1 @@
## Vanity files

15
lib/derfunke/example/header.cpp

@ -0,0 +1,15 @@
#include <derfunke.h>
void setup()
{
Serial.begin(115200);
// welcome serial output header
df_header_print();
}
void loop()
{
delay(500);
}

12
lib/derfunke/src/derfunke.cpp

@ -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();
}

5
lib/derfunke/src/derfunke.h

@ -0,0 +1,5 @@
#pragma once
#define MCUKIT_RELEASE "r12"
void df_header_print();

4
lib/easywifi/README.md

@ -0,0 +1,4 @@
# Easy Wifi
Wraps around Wifi connectivity to make it easy for students to connect their devices to wifi

24
lib/easywifi/example/client.cpp

@ -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() {
}

146
lib/easywifi/src/easywifi.cpp

@ -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

34
lib/easywifi/src/easywifi.h

@ -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__

3
lib/nanolog/README.md

@ -0,0 +1,3 @@
# Nanolog
Simple serial logging library for microcontrollers

194
lib/nanolog/src/nanolog.h

@ -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__

17
lib/ota/README.md

@ -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
```

38
lib/ota/example/otatest.cpp

@ -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
}

51
lib/ota/src/ota.cpp

@ -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();
}

7
lib/ota/src/ota.h

@ -0,0 +1,7 @@
#pragma once
#include <ArduinoOTA.h>
#include <Update.h>
void ota_init(const char *name);
void ota_loop();

5
lib/smoothie/README.md

@ -0,0 +1,5 @@
# Signal smoothing
A simple library to smooth values from analog readings in microcontroller projects.

25
lib/smoothie/example/compare.cpp

@ -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();
}

6
lib/smoothie/src/smoothie.cpp

@ -0,0 +1,6 @@
/**
* Signal smoothing utility functions
*
*/
#include <Arduino.h>
#include "Smoothie.h"

44
lib/smoothie/src/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__

6
lib/ugen/README.md

@ -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!

35
lib/ugen/examples/duoplot.cpp

@ -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();
}

338
lib/ugen/src/ugen.cpp

@ -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));
}
*/

57
lib/ugen/src/ugen.h

@ -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();
};

2
platformio.ini

@ -22,6 +22,8 @@ lib_deps =
SoftwareSerial=https://github.com/plerup/espsoftwareserial/archive/refs/tags/6.16.1.zip
Time=paulstoffregen/Time@^1.6.1
ESP32Servo=https://github.com/madhephaestus/ESP32Servo/archive/refs/tags/0.10.0.zip
MQTT = https://github.com/256dpi/arduino-mqtt
; NeoGPS=slashdevin/NeoGPS@^4.2.9
; SolarPosition=https://github.com/KenWillmott/SolarPosition.git

66
src/main.cpp

@ -1,18 +1,32 @@
#include <Arduino.h>
#if defined(ARDUINO_ARCH_ESP32)
#include <WiFi.h>
#include <ESP32Servo.h>
#else
#include <ESP8266WiFi.h>
#endif
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <easywifi.h>
#include <MQTT.h>
#include "settings.h"
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
#include <wmm.h>
#include <sunpos.h>
// the name of this device, we start with "unknown" and we will later generate one
String deviceId = "unknown";
WiFiClient net;
MQTTClient client(net);
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
@ -75,15 +89,67 @@ void init_servo() {
servo.attach(servoPin, 500, 2400);
}
void init_wifi() {
// connect to existing wifi access point as client
if( wifi_connect_as_client(WIFI_SSID, WIFI_PASSWORD) ) {
// print debugging information
wifi_print_mode();
wifi_print_ip(); // print our known ip address
} else {
Serial.print("Failed to connect to wifi ");
Serial.print( WIFI_SSID );
Serial.println();
}
}
void mqtt_on_message(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
}
void mqtt_connect() {
while (!client.connect(deviceId.c_str(), MQTT_BROKER_USER, MQTT_BROKER_PASS)) { // MQTT_DEVICE_ID,
Serial.print(".");
delay(1000);
}
client.begin(MQTT_BROKER, MQTT_PORT, net);
client.onMessage(mqtt_on_message);
}
/// create a random client ID
String mqtt_make_device_id() {
String retval;
// ESP32 version
uint64_t macAddress = ESP.getEfuseMac();
uint64_t macAddressTrunc = macAddress << 40;
uint64_t chipid = macAddressTrunc >> 40;
// this makes a random ID on every run, nice, but we don't really want that
// retval = String((int)chipid, HEX) + "-" + String(random(0xffff), HEX);
// so we make an ID that is bound to the device and stays the same on every run
// and we give it a fixed prefix so we can find all of our devices when we need them
retval = String("qi-") + String((int)chipid, HEX);
return retval;
}
// ///////////////////////////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////////////
void setup() {
Serial.begin(115200);
while(!Serial) ;
init_servo();
init_display();
init_gps();
wmm_init();
deviceId = mqtt_make_device_id();
init_wifi();
mqtt_connect();
}
void ui_nogpsfix() {

14
src/settings.h

@ -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__
Loading…
Cancel
Save