Kod programu:
Code: Select all
/*
Copyright (C) AC SOFTWARE SP. Z O.O.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/*Podłączenie:
I2C:
SDA - 21
SCL - 22
Enkoder:
CLK_PIN - 25
DATA_PIN - 26
SW_PIN - 27
Przycisk konfiguracyjny - 0
Dioda led konfiguracja - 13
termopara MAX6675:
DO - 19
CS - 5
CLK - 18
Wyjście PWM - 14
*/
//================================================Includy========================
#include "AiEsp32RotaryEncoder.h"
#include "Arduino.h"
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <PIDController.h>
#include "max6675.h"
#include <EEPROM.h>
#include <SuplaDevice.h>
#include <supla/network/esp_wifi.h>
#include <supla/control/relay.h>
#include <supla/control/button.h>
#include <supla/control/action_trigger.h>
#include <supla/device/status_led.h>
#include <supla/storage/littlefs_config.h>
#include <supla/network/esp_web_server.h>
#include <supla/device/supla_ca_cert.h>
#include <supla/events.h>
#include <supla/control/virtual_relay.h>
#include <supla/network/html/device_info.h>
#include <supla/network/html/protocol_parameters.h>
#include <supla/network/html/status_led_parameters.h>
#include <supla/network/html/wifi_parameters.h>
#include <supla/network/html/custom_parameter.h>
#include <supla/network/html/custom_text_parameter.h>
#include <supla/sensor/thermometer.h>
//===============================================================================
//===========================Definicje zmiennych=================================
#define STATUS_LED_GPIO 13
#define GPIO_Konfiguracja 0
int sda_pin = 21; // pin to connect to SDA pin on LCD (can be any data pin)
int scl_pin = 22; // pin to connect to SCL pin on LCD (can be any data pin)
double ThermostatTemperature;
unsigned long czasowka;
unsigned long czas_start;
bool doSaveTermSetTemp = false;
float Term_Step = 1;
double ThermostatHistereza;
float temperatura;
int polaczenie_z_serwerem;
int menu = 0;
// Define Rotary Encoder Pins
#define PIN_CLK 25
#define PIN_DT 26
#define PIN_SW 27
#define ROTARY_ENCODER_VCC_PIN -1
#define ROTARY_ENCODER_STEPS 4
//instead of changing here, rather change numbers above
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(PIN_CLK, PIN_DT, PIN_SW, ROTARY_ENCODER_VCC_PIN, ROTARY_ENCODER_STEPS);
#define thermoDO 19
#define thermoCS 5
#define thermoCLK 18
#define mosfet_pin 14
#define __DEBUG__
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
/*In this section we have defined the gain values for the
* proportional, integral, and derivative controller I have set
* the gain values with the help of trial and error methods.
*/
#define __Kp 30 // Proportional constant
#define __Ki 0.7 // Integral Constant
#define __Kd 200 // Derivative Constant
int clockPin; // Placeholder por pin status used by the rotary encoder
int clockPinState; // Placeholder por pin status used by the rotary encoder
long debounce = 0; // Debounce delay
int encoder_btn_count = 0; // used to check encoder button press
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);// Create an instance for the SSD1306 128X64 OLED "display"
PIDController pid; // Create an instance of the PID controller class, called "pid"
Supla::ESPWifi wifi;
Supla::EspWebServer suplaServer;
Supla::LittleFsConfig configSupla;
Supla::Device::StatusLed statusLed(STATUS_LED_GPIO, false);
Supla::Control::VirtualRelay *relay_0 = nullptr; // Automat / Ręczny
//==============================================================================================================
//==================== Przypisanie ustawionej temperatury pod klasę termometru =================================
//==============================================================================================================
class TermostatTemp : public Supla::Sensor::Thermometer {
public:
TermostatTemp() {}
void onInit() {
channel.setNewValue(getValue() );
}
double getValue() {
return ThermostatTemperature;
}
void iterateAlways() {
channel.setNewValue(getValue() );
if ( millis() - lastReadTime > 2000) {
lastReadTime = millis();
}
}
}; TermostatTemp *TerTemp = nullptr;
//==============================================================================================================
//======================== U S T A W I E N i E T E M P E R A T U R Y ========================================
//==============================================================================================================
class TermostatSet : public Supla::Control::Relay {
public:
TermostatSet() : Relay(-1, true, 32) {}
void onInit() {
// do nothing here
}
void turnOn(_supla_int_t duration) {
// increase "ThermostatTemperature" by Term_Step
ThermostatTemperature += Term_Step;
if (ThermostatTemperature > 125.0) ThermostatTemperature = 125.0;
channel.setNewValue(false);
doSaveTermSetTemp = true;
}
void turnOff(_supla_int_t duration) {
// decrease "ThermostatTemperature" by Term_Step
ThermostatTemperature -= Term_Step;
if (ThermostatTemperature < -20.0) ThermostatTemperature = -20.0;
channel.setNewValue(false);
doSaveTermSetTemp = true;
}
bool isOn() {
return false;
}
}; TermostatSet *TerSet = nullptr;
//==================Tutaj przekazujemy wartości z czujnika do Supli=====================================
class Temperatura : public Supla::Sensor::Thermometer {
public:
Temperatura() {}
void onInit() {
channel.setNewValue(getValue() );
}
double getValue()
{
return temperatura;
}
void iterateAlways() {
channel.setNewValue(getValue());
}
};
void status_func(int status, const char *msg) { // ------------------------ Status polaczednia z serwerem--------------------------
polaczenie_z_serwerem = status;
}
//=========================================================
void rotary_loop()
{
//dont print anything unless value changed
if (menu ==2 ){
if (rotaryEncoder.encoderChanged())
{
ThermostatTemperature = ThermostatTemperature + rotaryEncoder.readEncoder();
rotaryEncoder.reset();
}
}
}
void IRAM_ATTR readEncoderISR()
{
rotaryEncoder.readEncoder_ISR();
}
//=====================================================================
void setup() {
Serial.begin(115200);
EEPROM.begin(1024);
Wire.begin(sda_pin, scl_pin);
rotaryEncoder.begin();
rotaryEncoder.setup(readEncoderISR);
//set boundaries and if values should cycle or not
//in this example we will set possible values between 0 and 1000;
bool circleValues = false;
rotaryEncoder.setBoundaries(-1, 1, circleValues);
rotaryEncoder.disableAcceleration();
pinMode(mosfet_pin, OUTPUT); // MOSFET output PIN
pinMode(PIN_SW, INPUT_PULLUP);// Encoder SW Pin
pid.begin(); // initialize the PID instance
pid.setpoint(150); // The "goal" the PID controller tries to "reach"
pid.tune(__Kp, __Ki,__Kd); // Tune the PID, arguments: kP, kI, kD
pid.limit(0, 255); // Limit the PID output between 0 and 255, this is important to get rid of integral windup!
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
#ifdef __DEBUG__
Serial.println(F("SSD1306 allocation failed"));
#endif
for (;;); // Don't proceed, loop forever
}
auto przycisk_konfiguracja = new Supla::Control::Button(GPIO_Konfiguracja, false, false);
przycisk_konfiguracja->configureAsConfigButton(&SuplaDevice);
//================Supla przelacznik do ustawienia temperatury============================
TerSet = new TermostatSet();
TerSet->getChannel()->setDefault(SUPLA_CHANNELFNC_POWERSWITCH);
TerSet->disableChannelState();
new Temperatura(); // termometr
new TermostatTemp(); // ustawiona temperatura
EEPROM.get(0, ThermostatTemperature);
if ((ThermostatTemperature > 125.0) || isnan(ThermostatTemperature))
{
ThermostatTemperature = 20.0;
EEPROM.put(0, ThermostatTemperature);
EEPROM.commit();
}
SuplaDevice.setSuplaCACert(suplaCACert);
SuplaDevice.setSupla3rdPartyCACert(supla3rdCACert);
//======================status polaczenia z serwerem===================================
SuplaDevice.setStatusFuncImpl(&status_func);
//=====================================================================================
new Supla::Html::DeviceInfo(&SuplaDevice);
new Supla::Html::WifiParameters;
new Supla::Html::ProtocolParameters;
SuplaDevice.setName("SUPLA-Termostat");
SuplaDevice.begin();
display.setRotation(2); //Rotate the Display
display.display(); //Show initial display buffer contents on the screen -- the library initializes this with an Adafruit splash screen.
display.clearDisplay(); // Cleear the Display
display.setTextSize(2); // Set text Size
display.setTextColor(WHITE); // set LCD Colour
display.setCursor(48, 0); // Set Cursor Position
display.println("PID"); // Print the this Text
display.setCursor(0, 20); // Set Cursor Position
display.println("Regulator"); // Print the this Text
display.setCursor(22, 40); // Set Cursor Position
display.println("Temperatury"); // Print the this Text
display.display(); // Update the Display
delay(1000); // Delay of 200 ms
}
void loop()
{
SuplaDevice.iterate();
//read_encoder(); //Call The Read Encoder Function
//set_temp(); // Call the Set Temperature Function
if (doSaveTermSetTemp == true){
EEPROM.put(0, ThermostatTemperature);
EEPROM.commit();
Serial.println("wartosc temperatury zapisana");
doSaveTermSetTemp = false;
}
if (millis() > czasowka){
temperatura = thermocouple.readCelsius();
czasowka = millis() + 1000;
} // Read the Temperature using the readCelsius methode from MAX6675 Library.
int output = pid.compute(temperatura); // Let the PID compute the value, returns the optimal output
analogWrite(mosfet_pin, output); // Write the output to the output pin
pid.setpoint(ThermostatTemperature); // Use the setpoint methode of the PID library to
display.clearDisplay(); // Clear the display
display.setTextSize(2); // Set text Size
display.setCursor(0, 0); // Set the Display Cursor
display.print("Temp:"); //Print to the Display
display.setCursor(0, 16);// Set the Display Cursor
display.print(temperatura); // Print the Temperature value to the display in celcius
display.setCursor(0,32); // Set the Display Cursor
display.print("Ustaw:"); //Print to the Display
display.setCursor(0, 48);// Set the Display Cursor
display.print(ThermostatTemperature); // Print the Temperature value to the display in celcius
if ((menu == 2)||(menu == 3)){
display.setCursor(65,48); // Set the Display Cursor
display.print("<--"); //Print to the Display
}else{
display.setCursor(65,48);
display.print(" ");
}
display.display(); // Update the Display
#ifdef __DEBUG__
Serial.print(temperatura); // Print the Temperature value in *C on serial monitor
Serial.print(" "); // Print an Empty Space
Serial.println(output); // Print the Calculate Output value in the serial monitor.
#endif
rotary_loop();
// }
if (digitalRead(PIN_SW) == LOW) {
if ( menu == 0){
czas_start = (millis() + 3000);
menu = 1;
}
if ((menu == 1) && (millis() > czas_start)){
menu = 2;
}
if (menu == 2){
czas_start = (millis() + 3000);
menu = 3;
}
if ((menu == 3)&&(millis() > czas_start)){
menu = 0;
}
}
if((menu == 1) && (digitalRead(PIN_SW) == HIGH)){
menu = 0;
doSaveTermSetTemp = true;
}
if((menu == 3) && (digitalRead(PIN_SW) == HIGH)){
menu = 2;
}
}
Urządzenie zbudowane jest na ESP32. Pinologia i podłączenie są na początku programu. Kod programu, tak samo jak w większości moich kodów bazuje na przykładzie "Webinterface" z biblioteki SuplaDevice.
Ustawianie z poziomu urządzenia odbywa się następująco:
Wciskamy przycisk enkodera przez 3 sekundy pojawia się strzałka koło wartości zadanej i wtedy możemy ustawić zadaną wartość, wyjście z rybu ustawiania następuje tak samo, trzeba nacisnąc przez 3 sekundy przycisk enkodera.
Po każdorazowej zmianie wartości w trybie Online, oraz podczas wychodzenia z trybu ustawiania, aktualna wartość nastaw zapisywana jest w Epromie, tak aby po restarcie urządzenia pamiętało ono nastawy...