First Commit
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
10
.vscode/extensions.json
vendored
Normal file
10
.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
|
]
|
||||||
|
}
|
||||||
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"*.h": "cpp",
|
||||||
|
"cstdio": "cpp"
|
||||||
|
}
|
||||||
|
}
|
||||||
39
include/README
Normal file
39
include/README
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||||
229
lib/DHT/DHT.cpp
Normal file
229
lib/DHT/DHT.cpp
Normal file
@@ -0,0 +1,229 @@
|
|||||||
|
/*
|
||||||
|
* DHT Library for Digital-output Humidity and Temperature sensors
|
||||||
|
*
|
||||||
|
* Works with DHT11, DHT22
|
||||||
|
* SEN11301P, Grove - Temperature&Humidity Sensor (Seeed Studio)
|
||||||
|
* SEN51035P, Grove - Temperature&Humidity Sensor Pro (Seeed Studio)
|
||||||
|
* AM2302 , temperature-humidity sensor
|
||||||
|
* HM2303 , Digital-output humidity and temperature sensor
|
||||||
|
*
|
||||||
|
* Copyright (C) Wim De Roeve
|
||||||
|
* based on DHT22 sensor library by HO WING KIT
|
||||||
|
* Arduino DHT11 library
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documnetation 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
|
||||||
|
* furished 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 OR 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "DHT.h"
|
||||||
|
|
||||||
|
#define DHT_DATA_BIT_COUNT 40
|
||||||
|
|
||||||
|
DHT::DHT(PinName pin, eType DHTtype)
|
||||||
|
{
|
||||||
|
_pin = pin;
|
||||||
|
_DHTtype = DHTtype;
|
||||||
|
_firsttime = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DHT::~DHT()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
eError DHT::stall(DigitalInOut &io, int const level, int const max_time)
|
||||||
|
{
|
||||||
|
int cnt = 0;
|
||||||
|
while (level == io) {
|
||||||
|
if (cnt > max_time) {
|
||||||
|
|
||||||
|
return ERROR_NO_PATIENCE;
|
||||||
|
}
|
||||||
|
cnt++;
|
||||||
|
wait_us(1);
|
||||||
|
}
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
eError DHT::readData()
|
||||||
|
{
|
||||||
|
uint8_t i = 0, j = 0, b = 0, data_valid = 0;
|
||||||
|
uint32_t bit_value[DHT_DATA_BIT_COUNT] = {0};
|
||||||
|
|
||||||
|
eError err = ERROR_NONE;
|
||||||
|
time_t currentTime = time(NULL);
|
||||||
|
|
||||||
|
DigitalInOut DHT_io(_pin);
|
||||||
|
|
||||||
|
// IO must be in hi state to start
|
||||||
|
if (ERROR_NONE != stall(DHT_io, 0, 250)) {
|
||||||
|
return BUS_BUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start the transfer
|
||||||
|
DHT_io.output();
|
||||||
|
DHT_io = 0;
|
||||||
|
// only 500uS for DHT22 but 18ms for DHT11
|
||||||
|
(_DHTtype == 11) ? wait_us(18000) : wait_us(500);
|
||||||
|
DHT_io = 1;
|
||||||
|
wait_us(30);
|
||||||
|
DHT_io.input();
|
||||||
|
|
||||||
|
// wait till the sensor grabs the bus
|
||||||
|
if (ERROR_NONE != stall(DHT_io, 1, 40)) {
|
||||||
|
return ERROR_NOT_PRESENT;
|
||||||
|
}
|
||||||
|
// sensor should signal low 80us and then hi 80us
|
||||||
|
if (ERROR_NONE != stall(DHT_io, 0, 100)) {
|
||||||
|
return ERROR_SYNC_TIMEOUT;
|
||||||
|
}
|
||||||
|
if (ERROR_NONE != stall(DHT_io, 1, 100)) {
|
||||||
|
return ERROR_NO_PATIENCE;
|
||||||
|
}
|
||||||
|
// capture the data
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
for (j = 0; j < 8; j++) {
|
||||||
|
if (ERROR_NONE != stall(DHT_io, 0, 75)) {
|
||||||
|
return ERROR_DATA_TIMEOUT;
|
||||||
|
}
|
||||||
|
// logic 0 is 28us max, 1 is 70us
|
||||||
|
wait_us(40);
|
||||||
|
bit_value[i*8+j] = DHT_io;
|
||||||
|
if (ERROR_NONE != stall(DHT_io, 1, 50)) {
|
||||||
|
return ERROR_DATA_TIMEOUT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// store the data
|
||||||
|
for (i = 0; i < 5; i++) {
|
||||||
|
b=0;
|
||||||
|
for (j=0; j<8; j++) {
|
||||||
|
if (bit_value[i*8+j] == 1) {
|
||||||
|
b |= (1 << (7-j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DHT_data[i]=b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// uncomment to see the checksum error if it exists
|
||||||
|
//printf(" 0x%02x + 0x%02x + 0x%02x + 0x%02x = 0x%02x \n", DHT_data[0], DHT_data[1], DHT_data[2], DHT_data[3], DHT_data[4]);
|
||||||
|
data_valid = DHT_data[0] + DHT_data[1] + DHT_data[2] + DHT_data[3];
|
||||||
|
if (DHT_data[4] == data_valid) {
|
||||||
|
_lastReadTime = currentTime;
|
||||||
|
_lastTemperature = CalcTemperature();
|
||||||
|
_lastHumidity = CalcHumidity();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
err = ERROR_CHECKSUM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float DHT::CalcTemperature()
|
||||||
|
{
|
||||||
|
int v;
|
||||||
|
|
||||||
|
switch (_DHTtype) {
|
||||||
|
case DHT11:
|
||||||
|
v = DHT_data[2];
|
||||||
|
return float(v);
|
||||||
|
case DHT22:
|
||||||
|
v = DHT_data[2] & 0x7F;
|
||||||
|
v *= 256;
|
||||||
|
v += DHT_data[3];
|
||||||
|
v /= 10;
|
||||||
|
if (DHT_data[2] & 0x80)
|
||||||
|
v *= -1;
|
||||||
|
return float(v);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float DHT::ReadHumidity()
|
||||||
|
{
|
||||||
|
return _lastHumidity;
|
||||||
|
}
|
||||||
|
|
||||||
|
float DHT::ConvertCelciustoFarenheit(float const celsius)
|
||||||
|
{
|
||||||
|
return celsius * 9 / 5 + 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
float DHT::ConvertCelciustoKelvin(float const celsius)
|
||||||
|
{
|
||||||
|
return celsius + 273.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dewPoint function NOAA
|
||||||
|
// reference: http://wahiduddin.net/calc/density_algorithms.htm
|
||||||
|
float DHT::CalcdewPoint(float const celsius, float const humidity)
|
||||||
|
{
|
||||||
|
float A0= 373.15/(273.15 + celsius);
|
||||||
|
float SUM = -7.90298 * (A0-1);
|
||||||
|
SUM += 5.02808 * log10(A0);
|
||||||
|
SUM += -1.3816e-7 * (pow(10, (11.344*(1-1/A0)))-1) ;
|
||||||
|
SUM += 8.1328e-3 * (pow(10,(-3.49149*(A0-1)))-1) ;
|
||||||
|
SUM += log10(1013.246);
|
||||||
|
float VP = pow(10, SUM-3) * humidity;
|
||||||
|
float T = log(VP/0.61078); // temp var
|
||||||
|
return (241.88 * T) / (17.558-T);
|
||||||
|
}
|
||||||
|
|
||||||
|
// delta max = 0.6544 wrt dewPoint()
|
||||||
|
// 5x faster than dewPoint()
|
||||||
|
// reference: http://en.wikipedia.org/wiki/Dew_point
|
||||||
|
float DHT::CalcdewPointFast(float const celsius, float const humidity)
|
||||||
|
{
|
||||||
|
float a = 17.271;
|
||||||
|
float b = 237.7;
|
||||||
|
float temp = (a * celsius) / (b + celsius) + log(humidity/100);
|
||||||
|
float Td = (b * temp) / (a - temp);
|
||||||
|
return Td;
|
||||||
|
}
|
||||||
|
|
||||||
|
float DHT::ReadTemperature(eScale Scale)
|
||||||
|
{
|
||||||
|
if (Scale == FARENHEIT)
|
||||||
|
return ConvertCelciustoFarenheit(_lastTemperature);
|
||||||
|
else if (Scale == KELVIN)
|
||||||
|
return ConvertCelciustoKelvin(_lastTemperature);
|
||||||
|
else
|
||||||
|
return _lastTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
float DHT::CalcHumidity()
|
||||||
|
{
|
||||||
|
int v;
|
||||||
|
|
||||||
|
switch (_DHTtype) {
|
||||||
|
case DHT11:
|
||||||
|
v = DHT_data[0];
|
||||||
|
return float(v);
|
||||||
|
case DHT22:
|
||||||
|
v = DHT_data[0];
|
||||||
|
v *= 256;
|
||||||
|
v += DHT_data[1];
|
||||||
|
v /= 10;
|
||||||
|
return float(v);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
99
lib/DHT/DHT.h
Normal file
99
lib/DHT/DHT.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* DHT Library for Digital-output Humidity and Temperature sensors
|
||||||
|
*
|
||||||
|
* Works with DHT11, DHT21, DHT22
|
||||||
|
* SEN11301P, Grove - Temperature&Humidity Sensor (Seeed Studio)
|
||||||
|
* SEN51035P, Grove - Temperature&Humidity Sensor Pro (Seeed Studio)
|
||||||
|
* AM2302 , temperature-humidity sensor
|
||||||
|
* RHT01,RHT02, RHT03 , Humidity and Temperature Sensor (Sparkfun)
|
||||||
|
*
|
||||||
|
* Copyright (C) Wim De Roeve
|
||||||
|
* based on DHT22 sensor library by HO WING KIT
|
||||||
|
* Arduino DHT11 library
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documnetation 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
|
||||||
|
* furished 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 OR 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MBED_DHT_H
|
||||||
|
#define MBED_DHT_H
|
||||||
|
|
||||||
|
#include "mbed.h"
|
||||||
|
|
||||||
|
//typedef enum eType eType;
|
||||||
|
enum eType {
|
||||||
|
DHT11 = 11,
|
||||||
|
SEN11301P = 11,
|
||||||
|
RHT01 = 11,
|
||||||
|
DHT22 = 22,
|
||||||
|
AM2302 = 22,
|
||||||
|
SEN51035P = 22,
|
||||||
|
RHT02 = 22,
|
||||||
|
RHT03 = 22
|
||||||
|
};
|
||||||
|
|
||||||
|
//typedef enum eError eError;
|
||||||
|
enum eError {
|
||||||
|
ERROR_NONE = 0,
|
||||||
|
BUS_BUSY,
|
||||||
|
ERROR_NOT_PRESENT,
|
||||||
|
ERROR_ACK_TOO_LONG,
|
||||||
|
ERROR_SYNC_TIMEOUT,
|
||||||
|
ERROR_DATA_TIMEOUT,
|
||||||
|
ERROR_CHECKSUM,
|
||||||
|
ERROR_NO_PATIENCE
|
||||||
|
};
|
||||||
|
|
||||||
|
//typedef enum eScale eScale;
|
||||||
|
enum eScale {
|
||||||
|
CELCIUS = 0,
|
||||||
|
FARENHEIT,
|
||||||
|
KELVIN
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class DHT
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
DHT(PinName pin, eType DHTtype);
|
||||||
|
~DHT();
|
||||||
|
eError readData(void);
|
||||||
|
float ReadHumidity(void);
|
||||||
|
float ReadTemperature(eScale const Scale);
|
||||||
|
float CalcdewPoint(float const celsius, float const humidity);
|
||||||
|
float CalcdewPointFast(float const celsius, float const humidity);
|
||||||
|
|
||||||
|
private:
|
||||||
|
time_t _lastReadTime;
|
||||||
|
float _lastTemperature;
|
||||||
|
float _lastHumidity;
|
||||||
|
PinName _pin;
|
||||||
|
bool _firsttime;
|
||||||
|
eType _DHTtype;
|
||||||
|
uint8_t DHT_data[5];
|
||||||
|
float CalcTemperature();
|
||||||
|
float CalcHumidity();
|
||||||
|
float ConvertCelciustoFarenheit(float const);
|
||||||
|
float ConvertCelciustoKelvin(float const);
|
||||||
|
eError stall(DigitalInOut &io, int const level, int const max_time);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
324
lib/PID/pid.cpp
Normal file
324
lib/PID/pid.cpp
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
/**
|
||||||
|
* @author Aaron Berk
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* A PID controller is a widely used feedback controller commonly found in
|
||||||
|
* industry.
|
||||||
|
*
|
||||||
|
* This library is a port of Brett Beauregard's Arduino PID library:
|
||||||
|
*
|
||||||
|
* http://www.arduino.cc/playground/Code/PIDLibrary
|
||||||
|
*
|
||||||
|
* The wikipedia article on PID controllers is a good place to start on
|
||||||
|
* understanding how they work:
|
||||||
|
*
|
||||||
|
* http://en.wikipedia.org/wiki/PID_controller
|
||||||
|
*
|
||||||
|
* For a clear and elegant explanation of how to implement and tune a
|
||||||
|
* controller, the controlguru website by Douglas J. Cooper (who also happened
|
||||||
|
* to be Brett's controls professor) is an excellent reference:
|
||||||
|
*
|
||||||
|
* http://www.controlguru.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Includes
|
||||||
|
*/
|
||||||
|
#include "PID.h"
|
||||||
|
|
||||||
|
PID::PID(float Kc, float tauI, float tauD, float interval) {
|
||||||
|
|
||||||
|
usingFeedForward = false;
|
||||||
|
inAuto = false;
|
||||||
|
|
||||||
|
//Default the limits to the full range of I/O: 3.3V
|
||||||
|
//Make sure to set these to more appropriate limits for
|
||||||
|
//your application.
|
||||||
|
setInputLimits(0.0, 3.3);
|
||||||
|
setOutputLimits(0.0, 3.3);
|
||||||
|
|
||||||
|
tSample_ = interval;
|
||||||
|
|
||||||
|
setTunings(Kc, tauI, tauD);
|
||||||
|
|
||||||
|
setPoint_ = 0.0;
|
||||||
|
processVariable_ = 0.0;
|
||||||
|
prevProcessVariable_ = 0.0;
|
||||||
|
controllerOutput_ = 0.0;
|
||||||
|
prevControllerOutput_ = 0.0;
|
||||||
|
|
||||||
|
accError_ = 0.0;
|
||||||
|
bias_ = 0.0;
|
||||||
|
|
||||||
|
realOutput_ = 0.0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::setInputLimits(float inMin, float inMax) {
|
||||||
|
|
||||||
|
//Make sure we haven't been given impossible values.
|
||||||
|
if (inMin >= inMax) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rescale the working variables to reflect the changes.
|
||||||
|
prevProcessVariable_ *= (inMax - inMin) / inSpan_;
|
||||||
|
accError_ *= (inMax - inMin) / inSpan_;
|
||||||
|
|
||||||
|
//Make sure the working variables are within the new limits.
|
||||||
|
if (prevProcessVariable_ > 1) {
|
||||||
|
prevProcessVariable_ = 1;
|
||||||
|
} else if (prevProcessVariable_ < 0) {
|
||||||
|
prevProcessVariable_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inMin_ = inMin;
|
||||||
|
inMax_ = inMax;
|
||||||
|
inSpan_ = inMax - inMin;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::setOutputLimits(float outMin, float outMax) {
|
||||||
|
|
||||||
|
//Make sure we haven't been given impossible values.
|
||||||
|
if (outMin >= outMax) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Rescale the working variables to reflect the changes.
|
||||||
|
prevControllerOutput_ *= (outMax - outMin) / outSpan_;
|
||||||
|
|
||||||
|
//Make sure the working variables are within the new limits.
|
||||||
|
if (prevControllerOutput_ > 1) {
|
||||||
|
prevControllerOutput_ = 1;
|
||||||
|
} else if (prevControllerOutput_ < 0) {
|
||||||
|
prevControllerOutput_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
outMin_ = outMin;
|
||||||
|
outMax_ = outMax;
|
||||||
|
outSpan_ = outMax - outMin;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::setTunings(float Kc, float tauI, float tauD) {
|
||||||
|
|
||||||
|
//Verify that the tunings make sense.
|
||||||
|
if (Kc == 0.0 || tauI < 0.0 || tauD < 0.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Store raw values to hand back to user on request.
|
||||||
|
pParam_ = Kc;
|
||||||
|
iParam_ = tauI;
|
||||||
|
dParam_ = tauD;
|
||||||
|
|
||||||
|
float tempTauR;
|
||||||
|
|
||||||
|
if (tauI == 0.0) {
|
||||||
|
tempTauR = 0.0;
|
||||||
|
} else {
|
||||||
|
tempTauR = (1.0 / tauI) * tSample_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//For "bumpless transfer" we need to rescale the accumulated error.
|
||||||
|
if (inAuto) {
|
||||||
|
if (tempTauR == 0.0) {
|
||||||
|
accError_ = 0.0;
|
||||||
|
} else {
|
||||||
|
accError_ *= (Kc_ * tauR_) / (Kc * tempTauR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Kc_ = Kc;
|
||||||
|
tauR_ = tempTauR;
|
||||||
|
tauD_ = tauD / tSample_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::reset(void) {
|
||||||
|
|
||||||
|
float scaledBias = 0.0;
|
||||||
|
|
||||||
|
if (usingFeedForward) {
|
||||||
|
scaledBias = (bias_ - outMin_) / outSpan_;
|
||||||
|
} else {
|
||||||
|
scaledBias = (realOutput_ - outMin_) / outSpan_;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevControllerOutput_ = scaledBias;
|
||||||
|
prevProcessVariable_ = (processVariable_ - inMin_) / inSpan_;
|
||||||
|
|
||||||
|
//Clear any error in the integral.
|
||||||
|
accError_ = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::setMode(int mode) {
|
||||||
|
|
||||||
|
//We were in manual, and we just got set to auto.
|
||||||
|
//Reset the controller internals.
|
||||||
|
if (mode != 0 && !inAuto) {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
inAuto = (mode != 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::setInterval(float interval) {
|
||||||
|
|
||||||
|
if (interval > 0) {
|
||||||
|
//Convert the time-based tunings to reflect this change.
|
||||||
|
tauR_ *= (interval / tSample_);
|
||||||
|
accError_ *= (tSample_ / interval);
|
||||||
|
tauD_ *= (interval / tSample_);
|
||||||
|
tSample_ = interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::setSetPoint(float sp) {
|
||||||
|
|
||||||
|
setPoint_ = sp;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::setProcessValue(float pv) {
|
||||||
|
|
||||||
|
processVariable_ = pv;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void PID::setBias(float bias){
|
||||||
|
|
||||||
|
bias_ = bias;
|
||||||
|
usingFeedForward = 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::compute() {
|
||||||
|
|
||||||
|
//Pull in the input and setpoint, and scale them into percent span.
|
||||||
|
float scaledPV = (processVariable_ - inMin_) / inSpan_;
|
||||||
|
|
||||||
|
if (scaledPV > 1.0) {
|
||||||
|
scaledPV = 1.0;
|
||||||
|
} else if (scaledPV < 0.0) {
|
||||||
|
scaledPV = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float scaledSP = (setPoint_ - inMin_) / inSpan_;
|
||||||
|
if (scaledSP > 1.0) {
|
||||||
|
scaledSP = 1;
|
||||||
|
} else if (scaledSP < 0.0) {
|
||||||
|
scaledSP = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float error = scaledSP - scaledPV;
|
||||||
|
|
||||||
|
//Check and see if the output is pegged at a limit and only
|
||||||
|
//integrate if it is not. This is to prevent reset-windup.
|
||||||
|
if (!(prevControllerOutput_ >= 1 && error > 0) && !(prevControllerOutput_ <= 0 && error < 0)) {
|
||||||
|
accError_ += error;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Compute the current slope of the input signal.
|
||||||
|
float dMeas = (scaledPV - prevProcessVariable_) / tSample_;
|
||||||
|
|
||||||
|
float scaledBias = 0.0;
|
||||||
|
|
||||||
|
if (usingFeedForward) {
|
||||||
|
scaledBias = (bias_ - outMin_) / outSpan_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Perform the PID calculation.
|
||||||
|
controllerOutput_ = scaledBias + Kc_ * (error + (tauR_ * accError_) - (tauD_ * dMeas));
|
||||||
|
|
||||||
|
//Make sure the computed output is within output constraints.
|
||||||
|
if (controllerOutput_ < 0.0) {
|
||||||
|
controllerOutput_ = 0.0;
|
||||||
|
} else if (controllerOutput_ > 1.0) {
|
||||||
|
controllerOutput_ = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remember this output for the windup check next time.
|
||||||
|
prevControllerOutput_ = controllerOutput_;
|
||||||
|
//Remember the input for the derivative calculation next time.
|
||||||
|
prevProcessVariable_ = scaledPV;
|
||||||
|
|
||||||
|
//Scale the output from percent span back out to a real world number.
|
||||||
|
return ((controllerOutput_ * outSpan_) + outMin_);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::getInMin() {
|
||||||
|
|
||||||
|
return inMin_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::getInMax() {
|
||||||
|
|
||||||
|
return inMax_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::getOutMin() {
|
||||||
|
|
||||||
|
return outMin_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::getOutMax() {
|
||||||
|
|
||||||
|
return outMax_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::getInterval() {
|
||||||
|
|
||||||
|
return tSample_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::getPParam() {
|
||||||
|
|
||||||
|
return pParam_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::getIParam() {
|
||||||
|
|
||||||
|
return iParam_;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
float PID::getDParam() {
|
||||||
|
|
||||||
|
return dParam_;
|
||||||
|
|
||||||
|
}
|
||||||
213
lib/PID/pid.h
Normal file
213
lib/PID/pid.h
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/**
|
||||||
|
* @author Aaron Berk
|
||||||
|
*
|
||||||
|
* @section LICENSE
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 ARM Limited
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @section DESCRIPTION
|
||||||
|
*
|
||||||
|
* A PID controller is a widely used feedback controller commonly found in
|
||||||
|
* industry.
|
||||||
|
*
|
||||||
|
* This library is a port of Brett Beauregard's Arduino PID library:
|
||||||
|
*
|
||||||
|
* http://www.arduino.cc/playground/Code/PIDLibrary
|
||||||
|
*
|
||||||
|
* The wikipedia article on PID controllers is a good place to start on
|
||||||
|
* understanding how they work:
|
||||||
|
*
|
||||||
|
* http://en.wikipedia.org/wiki/PID_controller
|
||||||
|
*
|
||||||
|
* For a clear and elegant explanation of how to implement and tune a
|
||||||
|
* controller, the controlguru website by Douglas J. Cooper (who also happened
|
||||||
|
* to be Brett's controls professor) is an excellent reference:
|
||||||
|
*
|
||||||
|
* http://www.controlguru.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PID_H
|
||||||
|
#define PID_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Includes
|
||||||
|
*/
|
||||||
|
#include <mbed.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines
|
||||||
|
*/
|
||||||
|
#define MANUAL_MODE 0
|
||||||
|
#define AUTO_MODE 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proportional-integral-derivative controller.
|
||||||
|
*/
|
||||||
|
class PID {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* Sets default limits [0-3.3V], calculates tuning parameters, and sets
|
||||||
|
* manual mode with no bias.
|
||||||
|
*
|
||||||
|
* @param Kc - Tuning parameter
|
||||||
|
* @param tauI - Tuning parameter
|
||||||
|
* @param tauD - Tuning parameter
|
||||||
|
* @param interval PID calculation performed every interval seconds.
|
||||||
|
*/
|
||||||
|
PID(float Kc, float tauI, float tauD, float interval);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale from inputs to 0-100%.
|
||||||
|
*
|
||||||
|
* @param InMin The real world value corresponding to 0%.
|
||||||
|
* @param InMax The real world value corresponding to 100%.
|
||||||
|
*/
|
||||||
|
void setInputLimits(float inMin , float inMax);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scale from outputs to 0-100%.
|
||||||
|
*
|
||||||
|
* @param outMin The real world value corresponding to 0%.
|
||||||
|
* @param outMax The real world value corresponding to 100%.
|
||||||
|
*/
|
||||||
|
void setOutputLimits(float outMin, float outMax);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate PID constants.
|
||||||
|
*
|
||||||
|
* Allows parameters to be changed on the fly without ruining calculations.
|
||||||
|
*
|
||||||
|
* @param Kc - Tuning parameter
|
||||||
|
* @param tauI - Tuning parameter
|
||||||
|
* @param tauD - Tuning parameter
|
||||||
|
*/
|
||||||
|
void setTunings(float Kc, float tauI, float tauD);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reinitializes controller internals. Automatically
|
||||||
|
* called on a manual to auto transition.
|
||||||
|
*/
|
||||||
|
void reset(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set PID to manual or auto mode.
|
||||||
|
*
|
||||||
|
* @param mode 0 -> Manual
|
||||||
|
* Non-zero -> Auto
|
||||||
|
*/
|
||||||
|
void setMode(int mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set how fast the PID loop is run.
|
||||||
|
*
|
||||||
|
* @param interval PID calculation peformed every interval seconds.
|
||||||
|
*/
|
||||||
|
void setInterval(float interval);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the set point.
|
||||||
|
*
|
||||||
|
* @param sp The set point as a real world value.
|
||||||
|
*/
|
||||||
|
void setSetPoint(float sp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the process value.
|
||||||
|
*
|
||||||
|
* @param pv The process value as a real world value.
|
||||||
|
*/
|
||||||
|
void setProcessValue(float pv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the bias.
|
||||||
|
*
|
||||||
|
* @param bias The bias for the controller output.
|
||||||
|
*/
|
||||||
|
void setBias(float bias);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PID calculation.
|
||||||
|
*
|
||||||
|
* @return The controller output as a float between outMin and outMax.
|
||||||
|
*/
|
||||||
|
float compute(void);
|
||||||
|
|
||||||
|
//Getters.
|
||||||
|
float getInMin();
|
||||||
|
float getInMax();
|
||||||
|
float getOutMin();
|
||||||
|
float getOutMax();
|
||||||
|
float getInterval();
|
||||||
|
float getPParam();
|
||||||
|
float getIParam();
|
||||||
|
float getDParam();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
bool usingFeedForward;
|
||||||
|
bool inAuto;
|
||||||
|
|
||||||
|
//Actual tuning parameters used in PID calculation.
|
||||||
|
float Kc_;
|
||||||
|
float tauR_;
|
||||||
|
float tauD_;
|
||||||
|
|
||||||
|
//Raw tuning parameters.
|
||||||
|
float pParam_;
|
||||||
|
float iParam_;
|
||||||
|
float dParam_;
|
||||||
|
|
||||||
|
//The point we want to reach.
|
||||||
|
float setPoint_;
|
||||||
|
//The thing we measure.
|
||||||
|
float processVariable_;
|
||||||
|
float prevProcessVariable_;
|
||||||
|
//The output that affects the process variable.
|
||||||
|
float controllerOutput_;
|
||||||
|
float prevControllerOutput_;
|
||||||
|
|
||||||
|
//We work in % for calculations so these will scale from
|
||||||
|
//real world values to 0-100% and back again.
|
||||||
|
float inMin_;
|
||||||
|
float inMax_;
|
||||||
|
float inSpan_;
|
||||||
|
float outMin_;
|
||||||
|
float outMax_;
|
||||||
|
float outSpan_;
|
||||||
|
|
||||||
|
//The accumulated error, i.e. integral.
|
||||||
|
float accError_;
|
||||||
|
//The controller output bias.
|
||||||
|
float bias_;
|
||||||
|
|
||||||
|
//The interval between samples.
|
||||||
|
float tSample_;
|
||||||
|
|
||||||
|
//Controller output as a real world value.
|
||||||
|
volatile float realOutput_;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* PID_H */
|
||||||
46
lib/README
Normal file
46
lib/README
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||||
10
mbed_app.json
Normal file
10
mbed_app.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"target_overrides": {
|
||||||
|
"*": {
|
||||||
|
"mbed-trace.enable": false,
|
||||||
|
"mbed-trace.max-level": "TRACE_LEVEL_DEBUG",
|
||||||
|
"rtos.main-thread-stack-size": 8192,
|
||||||
|
"target.printf_lib": "std"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
platformio.ini
Normal file
14
platformio.ini
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
|
||||||
|
[env:nucleo_f401re]
|
||||||
|
platform = ststm32
|
||||||
|
board = nucleo_f401re
|
||||||
|
framework = mbed
|
||||||
29
src/main.cpp
Normal file
29
src/main.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include <mbed.h>
|
||||||
|
#include <pid.h>
|
||||||
|
#include <DHT.h>
|
||||||
|
|
||||||
|
auto t_controller = PID(1, 1, 0, 2);
|
||||||
|
auto t_sensor = DHT(PC_10, AM2302);
|
||||||
|
auto led = DigitalOut(PA_5);
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
printf("Start\n");
|
||||||
|
t_controller.setInputLimits(10,30);
|
||||||
|
t_controller.setOutputLimits(0,100);
|
||||||
|
t_controller.setSetPoint(29);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (eError::ERROR_NONE != t_sensor.readData()){
|
||||||
|
printf("Error\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto t = t_sensor.ReadTemperature(CELCIUS);
|
||||||
|
auto h = t_sensor.ReadHumidity();
|
||||||
|
t_controller.setProcessValue(t);
|
||||||
|
auto c = t_controller.compute();
|
||||||
|
printf("T:%3.1f\tH:%3.1f\tCV:%3.1f\n",t,h,c);
|
||||||
|
led = !led;
|
||||||
|
ThisThread::sleep_for(2s);
|
||||||
|
}
|
||||||
|
}
|
||||||
11
test/README
Normal file
11
test/README
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PlatformIO Test Runner and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PlatformIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
|
||||||
Reference in New Issue
Block a user