Initial Upload
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Blynk Connection Settings
|
||||
*/
|
||||
|
||||
//Uncomment to use your own server
|
||||
#define useLocalServer
|
||||
|
||||
char auth[35] = "";
|
||||
char server[] = "";
|
||||
int port = 8080;
|
||||
+23
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
struct Temperature {
|
||||
float cur;
|
||||
float target;
|
||||
float offset;
|
||||
int mode;
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user