Initial Upload

This commit is contained in:
mrcory
2020-03-01 16:56:07 -05:00
parent 5a8b228c5c
commit eb554b2c23
7 changed files with 591 additions and 0 deletions
+154
View File
@@ -0,0 +1,154 @@
/* Sonoff Smart Plug powered heater controller.
*
* This programed is to be flashed onto a Sonoff S31 smart plug
* to allow controlling a "dumb" heater that uses a mechanical thermostat.
*
* This allows remote control and monitoring of a heater that would
* normally not have these features. Being a discrete addition, this
* feature set can be moved from heater to heater.
*
* I am building upon code from the below link.
* The Wemo functions have been removed.
*
* https://ucexperiment.wordpress.com/2019/01/20/using-a-sonoff-s31-with-blynk/
* Author: James Eli
* Date: 1/20/2019
*/
int configVersion = 0;
#include <ESP8266WiFi.h>
#include <Ethernet.h>
#include <ESP8266mDNS.h>
#include <WiFiManager.h>
#include <ESP8266WebServer.h>
#include <BlynkSimpleEsp8266.h>
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#include <EEPROM.h>
bool isFirstRun = true;
bool isFirstConnect = true; // Flag for re-sync on connection.
int relayState = LOW; // Blynk app pushbutton status.
boolean SwitchReset = true;
// esp8266 pins.
#define ESP8266_GPIO13 13 // Sonof green LED (LOW == ON).
#define ESP8266_GPIO0 0 // Sonoff pushbutton (LOW == pressed).
#define ESP8266_GPIO12 12 // Sonoff relay (HIGH == ON).
const int RELAY = ESP8266_GPIO12; // Relay switching pin. Relay is pin 12 on the SonOff
const int LED = ESP8266_GPIO13; // On/off indicator LED. Onboard LED is 13 on Sonoff
const int SWITCH = ESP8266_GPIO0; // Pushbutton.
// CSE7766 data.
double power = 0;
double voltage = 0;
double current = 0;
double energy = 0;
double ratioV = 1.0;
double ratioC = 1.0;
double ratioP = 1.0;
// Serial data input buffer.
unsigned char serialBuffer[24];
// Serial error flags.
int error;
// Energy reset counter.
int energyResetCounter;
#define MAX_ENREGY_RESET_COUNT 12
// CSE7766 error codes.
#define SENSOR_ERROR_OK 0 // No error.
#define SENSOR_ERROR_OUT_OF_RANGE 1 // Result out of sensor range.
#define SENSOR_ERROR_WARM_UP 2 // Sensor is warming-up.
#define SENSOR_ERROR_TIMEOUT 3 // Response from sensor timed out.
#define SENSOR_ERROR_UNKNOWN_ID 4 // Sensor did not report a known ID.
#define SENSOR_ERROR_CRC 5 // Sensor data corrupted.
#define SENSOR_ERROR_I2C 6 // Wrong or locked I2C address.
#define SENSOR_ERROR_GPIO_USED 7 // The GPIO is already in use.
#define SENSOR_ERROR_CALIBRATION 8 // Calibration error or not calibrated.
#define SENSOR_ERROR_OTHER 99 // Any other error.
#define CSE7766_V1R 1.0 // 1mR current resistor.
#define CSE7766_V2R 1.0 // 1M voltage resistor.
//-------------------------------
bool relayOveride = false;
#include "config.h"
#include "timer.h"
#include "blynk.h"
#include "blynkFunc.h"
#include "function.h"
void setup() {
Serial.begin(74880);
// WiFiManager intialization.
WiFiManager wifi;
// Initialize pins.
pinMode( RELAY, OUTPUT );
pinMode( LED, OUTPUT );
pinMode( SWITCH, INPUT_PULLUP );
delay( 10 );
// Switch relay off, LED on.
digitalWrite( RELAY, LOW );
digitalWrite( LED, LOW );
// Create AP, if necessary
wifi.autoConnect(deviceName);
// Set WIFI module to STA mode
WiFi.mode( WIFI_STA );
// Initialize Blynk.
#ifdef useLocalServer
Blynk.config(auth,server,port);
#else
Blynk.config(auth);
#endif
// Set ota details.
ArduinoOTA.setHostname(deviceName);
ArduinoOTA.begin();
//Call timer setup from timer.h
timerSetup();
// Switch LED off to signal initialization complete.
digitalWrite( LED, HIGH );
EEPROM.begin(256); //Initialize the EEPROM
if (EEPROM.read(0) == 1 && returnConfigVersion() == configVersion) { //If flagged in EEPROM; we load our current position
configLoad();
Serial.println("Pass Config Version Check");
} else {
Serial.println("Fail Config Version Check, Reseting To Default");
configSave();
}
}
void loop() {
if (timer(2,2000)) {
sendBlynk();
}
ArduinoOTA.handle();
Blynk.run();
process();
if (!relayOveride) {
tempProcess();
}
if (isFirstRun == true) {
isFirstRun = false;
}
}
+10
View File
@@ -0,0 +1,10 @@
/*
* Blynk Connection Settings
*/
//Uncomment to use your own server
#define useLocalServer
char auth[35] = "";
char server[] = "";
int port = 8080;
+23
View File
@@ -0,0 +1,23 @@
/* V59 = relayOveride feedback to app
*
*/
BLYNK_WRITE(V50) {
temp.cur = param.asInt(); //Get temp from other device
}
BLYNK_WRITE(V59) {
if (param.asInt() != relayOveride) {
relayOveride = param.asInt();
}
}
void sendBlynk() { //Feedback to the Blynk app
Blynk.virtualWrite(V51,temp.cur);
if (isFirstRun) {
Blynk.virtualWrite(V53,temp.target);
Blynk.virtualWrite(V55,temp.offset);
Blynk.virtualWrite(V57,cooldownPeriod);
}
}
+21
View File
@@ -0,0 +1,21 @@
#include "structs.h"
//Device name should be unique as it is used
//to identify the device for OTA updates.
#define deviceName "Heater"
//Blynk token for the device that will be providing
//the temperature.
#define tempDeviceToken ""
//Temerature settings
Temperature temp = {
90, //Current Temp (Don't change)
0 , //Target Temp
2 , //Temp Range
0 , //Mode (0:F or 1:C) (Not coded)
};
//This amount of time must pass before the heater is allowd to turn back on (in seconds)
//Though this shouldn't be needed with the temp.offset, I wished to add it anyway.
int cooldownPeriod = 30;
+325
View File
@@ -0,0 +1,325 @@
//---------------------------------------------------------
//Orignal code functions
// Toggle the relay on
void RelayOn()
{
digitalWrite( RELAY, HIGH );
relayState = true;
Blynk.virtualWrite( V0, HIGH ); // Sync the Blynk button widget state
Blynk.virtualWrite( V1, relayState*255 );
}
// Toggle the relay off
void RelayOff()
{
digitalWrite( RELAY, LOW );
relayState = false;
Blynk.virtualWrite( V0, LOW ); // Sync the Blynk button widget state
Blynk.virtualWrite( V1, relayState*255 );
}
// Handle switch changes originating on the Blynk app
BLYNK_WRITE( V0 )
{
int SwitchStatus = param.asInt();
if ( SwitchStatus )
RelayOn();
else
RelayOff();
}
// This function runs every time Blynk connection is established.
BLYNK_CONNECTED()
{
if ( isFirstConnect )
{
Blynk.syncAll();
isFirstConnect = false;
}
}
// Handle hardware switch activation.
void ButtonCheck()
{
// look for new button press
boolean SwitchState = ( digitalRead( SWITCH ) );
// toggle the switch if there's a new button press
if ( !SwitchState && SwitchReset == true )
{
if ( relayState )
RelayOff();
else
RelayOn();
// Flag that indicates the physical button hasn't been released
SwitchReset = false;
delay( 50 ); // De-bounce interlude.
relayOveride = true; //If button is used, overide the temperature control
Blynk.virtualWrite(V59,relayOveride); //Feedback to the app
}
else if ( SwitchState )
{
// reset flag the physical button release
SwitchReset = true;
}
}
// Relay toggle helper function.
void ToggleRelay()
{
relayState = !relayState;
if ( relayState )
RelayOn();
else
RelayOff();
}
// CSE7766 checksum.
bool CheckSum()
{
unsigned char checksum = 0;
for (unsigned char i = 2; i < 23; i++)
checksum += serialBuffer[i];
return checksum == serialBuffer[23];
}
// Process a cse7766 data packet.
void ProcessCse7766Packet()
{
// Confirm packet checksum.
if ( !CheckSum() )
{
error = SENSOR_ERROR_CRC;
return;
}
// Check for calibration error.
if ( serialBuffer[0] == 0xAA )
{
error = SENSOR_ERROR_CALIBRATION;
return;
}
if ( (serialBuffer[0] & 0xFC) == 0xFC )
{
error = SENSOR_ERROR_OTHER;
return;
}
// Retrieve calibration coefficients.
unsigned long coefV = (serialBuffer[2] << 16 | serialBuffer[3] << 8 | serialBuffer[4] );
unsigned long coefC = (serialBuffer[8] << 16 | serialBuffer[9] << 8 | serialBuffer[10]);
unsigned long coefP = (serialBuffer[14] << 16 | serialBuffer[15] << 8 | serialBuffer[16]);
uint8_t adj = serialBuffer[20];
// Calculate voltage.
voltage = 0;
if ( (adj & 0x40) == 0x40 )
{
unsigned long voltageCycle = serialBuffer[5] << 16 | serialBuffer[6] << 8 | serialBuffer[7];
voltage = ratioV*coefV/voltageCycle/CSE7766_V2R;
}
// Calculate power.
power = 0;
if ( (adj & 0x10) == 0x10 )
{
if ( (serialBuffer[0] & 0xF2) != 0xF2 )
{
unsigned long powerCycle = serialBuffer[17] << 16 | serialBuffer[18] << 8 | serialBuffer[19];
power = ratioP*coefP/powerCycle/CSE7766_V1R/CSE7766_V2R;
}
}
// Calculate current.
current = 0;
if ( (adj & 0x20) == 0x20 )
{
if ( power > 0 )
{
unsigned long currentCycle = serialBuffer[11] << 16 | serialBuffer[12] << 8 | serialBuffer[13];
current = ratioC*coefC/currentCycle/CSE7766_V1R;
}
}
// Calculate energy.
unsigned int difference;
static unsigned int cfPulsesLast = 0;
unsigned int cfPulses = serialBuffer[21] << 8 | serialBuffer[22];
if (0 == cfPulsesLast)
cfPulsesLast = cfPulses;
if (cfPulses < cfPulsesLast)
difference = cfPulses + (0xFFFF - cfPulsesLast) + 1;
else
difference = cfPulses - cfPulsesLast;
energy += difference*(float)coefP/1000000.0;
cfPulsesLast = cfPulses;
// Energy reset.
if ( power == 0 )
energyResetCounter++;
else
energyResetCounter = 0;
if ( energyResetCounter >= MAX_ENREGY_RESET_COUNT )
{
energy = 0.0;
energyResetCounter = 0;
}
// Push data to Blynk app.
Blynk.virtualWrite( V3, voltage ); // Voltage (Volts).
Blynk.virtualWrite( V4, current ); // Current (Amps).
Blynk.virtualWrite( V5, power ); // Power (Watts).
Blynk.virtualWrite( V6, energy ); // Energy (kWh).
}
// Read serial cse7766 power monitor data packet.
void ReadCse7766()
{
// Assume a non-specific error.
error = SENSOR_ERROR_OTHER;
static unsigned char index = 0;
while ( Serial.available() > 0 )
{
uint8_t input = Serial.read();
// first byte must be 0x55 or 0xF?.
if ( index == 0 )
{
if ( (input != 0x55) && (input < 0xF0) )
continue;
}
// second byte must be 0x5A.
else if ( index == 1 )
{
if ( input != 0x5A )
{
index = 0;
continue;
}
}
serialBuffer[index++] = input;
if ( index > 23 )
{
Serial.flush();
break;
}
}
// Process packet.
if ( index == 24 )
{
error = SENSOR_ERROR_OK;
ProcessCse7766Packet();
index = 0;
}
// Report error state (LED) of cse7766.
if ( error == SENSOR_ERROR_OK )
Blynk.virtualWrite( V18, 0 );
else
Blynk.virtualWrite( V18, 255 );
}
//---------------------------------
//Basic functions that must run
void process() {
if (timer(0,1000)) {
ReadCse7766();
}
if (timer(1,100)) {
ButtonCheck();
}
}
bool cooldownCheck() {
if (timerCheck(3) >= cooldownPeriod*1000) {
timerReset(3);
return true;
} else {
return false;
}
}
void tempProcess() {
if (temp.cur < temp.target && relayState == false) { //If colder than set do next check
if (cooldownCheck() == true && temp.cur < temp.target - temp.offset) { //If cooldown has passed and temp is below turn on temp, turn on
RelayOn();
}
} else {
if (relayState == true) {
RelayOff();
}
}
}
int configWritePos = 10;
void quickPut(int _target) {
EEPROM.put(configWritePos,_target);
configWritePos+=sizeof(_target);
}
int quickGet() {
int _temp;
EEPROM.get(configWritePos,_temp);
configWritePos+=sizeof(_temp);
return _temp;
}
void quickPutFloat(float _target) {
EEPROM.put(configWritePos,_target);
configWritePos+=sizeof(_target);
}
int quickGetFloat() {
float _temp;
EEPROM.get(configWritePos,_temp);
configWritePos+=sizeof(_temp);
return _temp;
}
void configSave() {
EEPROM.write(0,1); //Flag for autoload
EEPROM.put(3,configVersion);
configWritePos = 10;
quickPutFloat(temp.target);
quickPutFloat(temp.offset);
quickPut(temp.mode);
quickPut(cooldownPeriod);
EEPROM.commit();
Serial.println("Config Saved");
}
void configLoad() {
configWritePos = 10;
temp.target = quickGetFloat();
temp.offset = quickGetFloat();
temp.mode = quickGet();
cooldownPeriod = quickGet();
Serial.println("Config Loaded");
}
byte returnConfigVersion() {
byte grabbedVersion;
EEPROM.get(3,grabbedVersion);
return grabbedVersion;
}
+6
View File
@@ -0,0 +1,6 @@
struct Temperature {
float cur;
float target;
float offset;
int mode;
};
+52
View File
@@ -0,0 +1,52 @@
/*Timer code
* 0 - Reads from sensor & gets temp from bridged device
* 1 - Reads button
* 2 - Update Blynk app data
* 3 - Heater trigger cooldown
*/
#define countDataAmount 4 //Number of timers
unsigned long countData[countDataAmount] = {millis()}; //Holds count information. (Adjust for numeber of timers needed.)
//Used by timer function
unsigned long millisCount(int _mode, int _id) { //_mode: 0-Start 1-Stop | _id Identity number (allow more by editing the length of countData
unsigned long _count;
if (_mode == 0) {
countData[_id]= millis();
return 0;
}
if (_mode == 1) {
_count = millis() - countData[_id];
return _count;
}
}
//Will set all variables in timer table to 0.
void timerSetup() {
for (int i=0;i<countDataAmount;i++) {
millisCount(0,i);
}
}
//Use this to check if set time has passed.
//Will return true or false
bool timer(unsigned long _interval,int _id) { //_interval in millis, _id in countData
if (millisCount(1,_id) >= _interval) {
millisCount(0,_id);
return true;
} else {
return false;
}
}
unsigned long timerCheck(int _id) { //Simply return the elapsed count
return millisCount(1,_id);
}
void timerReset(byte _id) { //Reset timer start time
countData[_id]= millis();
}