/*
* Project CurrentMonitor
*
* Description: This is program 1 of 2 for the AutoVenitlation project.
* Current-monitoring sensors will be placed on each power cable of two Epilog Laser Unit machines.
* When the state of power changes (from ON to OFF or OFF to ON) on either or both Epilog units, this
* program will PUBLISH the state change to the Adafruit dashboard.
* This program's sole purpose is to monitor the power and PUBLISH this state change.
*
* There will be a second companion program to this one that will SUBSCRIBE to the same dashboard. When
* a state change is detected, that second program will send a signal to a relay that will either turn on or turn off
* an exhaust fan.
*
* Author: Clint Wolf
*
* Date: Project inception in September 2021
*/
#include <JsonParserGeneratorRK.h> // Searched for and installed
#include <Particle.h> // Search yielded no results. Did not install, but compiled successfully anyway
#include <Wire.h> // Search yielded no results. Did not install, but compiled successfully anyway
#include <neopixel.h> // Searched for neopixel. Found and installed
// Adafruit.io Set Up BEGIN
#include <Adafruit_MQTT.h> // Searched for Adafruit_MQTT. Found and installed
// All the below may come with the above include statement, but not sure if it is available only after
// the above executes.
#include "Adafruit_MQTT/Adafruit_MQTT.h"
#include "Adafruit_MQTT/Adafruit_MQTT_SPARK.h"
#include "Adafruit_MQTT/Adafruit_MQTT.h"
/************************* Adafruit.io Setup ******************************************/
#define AIO_SERVER "io.adafruit.com"
#define AIO_SERVERPORT 1883 // use 1883 for SSL
#define AIO_USERNAME "IoTLabs"
#define AIO_KEY "aio_REBO83agD50Mqh9VohnAzJcOK5ky"
/************ Global State (you don't need to change this!, per Brian so don't change it) ************/
TCPClient TheClient;
// Setup the MQTT client class by passing in the WiFi client, MQTT server, and login details
Adafruit_MQTT_SPARK mqtt(&TheClient,AIO_SERVER,AIO_SERVERPORT,AIO_USERNAME,AIO_KEY);
/****************************** Feeds ***************************************/
// Setup a feed called <object> for publishing.
// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
//Adafruit_MQTT_Publish theTempuratureObject = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/Feed2_Tempurature");
Adafruit_MQTT_Publish theCurrentSensorOneStateObject = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/CurrentSensorOneState");
Adafruit_MQTT_Publish theCurrentSensorOneActualValue = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/CurrentSensorOneActualValue");
Adafruit_MQTT_Publish theCurrentSensorTwoStateObject = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/CurrentSensorTwoState");
Adafruit_MQTT_Publish theCurrentSensorTwoActualValue = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/CurrentSensorTwoActualValue");
Adafruit_MQTT_Publish sensorOneState = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/fuse.epilogone");
Adafruit_MQTT_Publish sensorOneValue = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/fuse.epilogonevalue");
Adafruit_MQTT_Publish sensorTwoState = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/fuse.epilogtwo");
Adafruit_MQTT_Publish sensorTwoValue = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "/feeds/fuse.epilogtwovalue");
// Adafruit.io Set Up END
// For the neopixels: Pixel 0 is for sensor one, pixel 1 is for sensor two
const int PIN = 2; // Pin 2 on Argon for output signal to neopixels
const int NUMPIXELS = 2; // the actual number of neopixels
//Adafruit_NeoPixel thePixelObject(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
//supports WS2811/WS2812/WS2813
Adafruit_NeoPixel thePixelObject(NUMPIXELS, PIN, WS2812B);
//neopixel thePixelObject(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
// Constructor: number of LEDs, pin number, LED type
const int pixelDelay=250; // just a variable I created to delay
const int startPixel=0; // the first pixel is 0 (zero based)
int var_PixelBrightness = 200; // the brightness value can go from 1 to 255
// thePixelObject.Color() takes RGB values from 0,0,0, to 255,255,255 - or a defined color from an included COLORS file
//int var_PixelColor = 0x008000;
// The following define the value ranges for the neopixels
// Green indicates an Epilog unit that is lasing (when the value is greater than the Yellow HIGH value)
// Yellow indicates an Epilog unit that is on but not lasing
// Red indicates an Epilog unit that is powered off (when the value is less than the Yellow LOW value)
float var_YellowValue_High = 0.99999;
float var_YellowValue_Low = 0.45000;
float var_SensorValueToPublish = 0.0; //value read from sensor
float var_SensorActualValue = 0.0; // used to store the Actual value until it can be sent to the dashboard
// these are the variables used to store the power state of each Epilog laser unit
int var_PowerStateHoldOne;
int var_PowerStateHoldTwo;
// The broker's server may sever the connection if a publish event is not done prior to the default of 5 minutes.
// This variable will capture the elapsed time since last publish and
// if that elapsed time >= 2 minutes, the program will ping the server.
int var_LastServerPing = 0;
int var_LastCurrentSensor1Read = 0;
int var_LastCurrentSensor2Read = 0;
float var_SensorMisreadValue = 16777.21;
// Start of github original Brian code
// PECMAC125A I2C address is 0x2A(42)
#define Addr 0x2A
byte data[36];
int typeOfSensor, maxCurrent, noOfChannel, i;
//float current = 0.0;
//unsigned int lastTime,last;
//SYSTEM_MODE(SEMI_AUTOMATIC);
// End of github original Brian code
//====================================================================================
SYSTEM_THREAD(ENABLED);
void setup()
{
// Put initialization like pinMode and begin functions here.
Serial.begin(9600);
//while(!Serial);
waitFor(Serial.isConnected, 15000);
delay(3000);
Serial.println("The CurrentMonitor program has activated the Serial port.");
pinMode(2,OUTPUT); // used for signal for neopixels
thePixelObject.begin();
thePixelObject.clear(); // sets all pixels' colors to 'off' - to initialize ALL pixels
// Initiate the Wire library and join the I2C bus as a master or slave. This should normally be called only once.
// address: the 7-bit slave address (optional); if not specified, join the bus as a master.
Wire.begin();
//Connect to WiFi but not Particle Cloud
WiFi.connect();
while(WiFi.connecting())
{
Serial.printf(".");
delay(250);
}
Serial.printf("\n");
currentInit(); // call to initialize NCD board hosting current sensors
// Request 6 bytes of data
Wire.requestFrom(Addr, 6);
// Read 6 bytes of data
if (Wire.available() == 6)
{
for(i=0;i<6;i++)
{
data[i] = Wire.read();
}
}
typeOfSensor = data[0];
maxCurrent = data[1];
noOfChannel = data[2];
// Output data to Serial Monitor
Serial.printf("Type of Sensor %i \n",typeOfSensor);
Serial.printf("Max Current: %i \n", maxCurrent);
Serial.printf("No. of Channels: %i \n", noOfChannel);
delay(5000);
}
//====================================================================================
void loop()
{
MQTT_connect();
// The default "keep-alive" timeframe is 5 minutes, or 300 seconds.
// I will check for a publish event and if none has occured in the last 2
// minutes, I will ping the server.
if ((millis()-var_LastServerPing) > 120000) // (120 seconds)
{
Serial.println("Pinging the MQTT server");
// ping the server to keep the mqtt connection alive
if(! mqtt.ping()) // if I cannot ping the server, force a disconnect
{
Serial.println("Forcing an MQTT disconnect");
mqtt.disconnect(); // forcing a disconnect will force a reconnection
Serial.println("Attempting to reestablish MQTT connection");
}
var_LastServerPing = millis();
}
ReadSensorOne();
ReadSensorTwo();
delay(1); // just enough of a delay to allow completion of other tasks
}
//================================================================================
void ReadSensorOne()
{
if((millis()-var_LastCurrentSensor1Read) > 15000) // wait 10 seconds before trying to read the sensor again
{
if(mqtt.Update())
{
var_SensorActualValue = getCurrent(Addr,1);
Serial.printf("Current Value of Sensor One: %0.5f \n", var_SensorActualValue);
if (var_SensorActualValue > var_YellowValue_High)
// sensor reads an ON and LASING state
{
var_SensorValueToPublish = 1;
ModifyNeoPixel(1, 0x00FF00); // make it green
}
if ((var_SensorActualValue <= var_YellowValue_High) && (var_SensorActualValue >= var_YellowValue_Low))
// sensor reads an ON, but not LASING state
{
var_SensorValueToPublish = 1;
ModifyNeoPixel(1, 0xFFFF00); // make it yellow
}
if (var_SensorActualValue < var_YellowValue_Low)
// sensor reads an OFF state
{
var_SensorValueToPublish = 0;
ModifyNeoPixel(1, 0xFF0000); // make it red
}
// This next code segment is for a sensor misread - the value would be greater than 16,000
// so I set the Sensor value to 0 to stop the exhaust fan from coming on unnecessarily
if (var_SensorActualValue == var_SensorMisreadValue) // sensor reads an ON state
{
var_SensorValueToPublish = 0;
ModifyNeoPixel(1, 0xFF0000); // make it red
}
PublishToDashboard(1, var_SensorValueToPublish, var_SensorActualValue);
//PublishToDashboard(1, var_SensorValue);
}
var_LastCurrentSensor1Read = millis();
}
}
//================================================================================
void ReadSensorTwo()
{
if((millis()-var_LastCurrentSensor2Read) > 15000) // wait 10 seconds before trying to read the sensor again
{
if(mqtt.Update())
{
var_SensorActualValue = getCurrent(Addr,2);
Serial.printf(" Current Value of Sensor Two: %0.5f \n", var_SensorActualValue);
if (var_SensorActualValue > var_YellowValue_High)
// sensor reads an ON and LASING state
{
var_SensorValueToPublish = 1;
ModifyNeoPixel(0, 0x00FF00); // make it green
}
if ((var_SensorActualValue <= var_YellowValue_High) && (var_SensorActualValue >= var_YellowValue_Low))
// sensor reads an ON, but not LASING state
{
var_SensorValueToPublish = 1;
ModifyNeoPixel(0, 0xFFFF00); // make it yellow
}
if (var_SensorActualValue < var_YellowValue_Low)
// sensor reads an OFF state
{
var_SensorValueToPublish = 0;
ModifyNeoPixel(0, 0xFF0000); // make it red
}
// This next code segment is for a sensor misread - the value would be greater than 16,000
// so I set the Sensor value to 0 to stop the exhaust fan from coming on unnecessarily
if (var_SensorActualValue == var_SensorMisreadValue) // sensor reads an ON state
{
var_SensorValueToPublish = 0;
ModifyNeoPixel(0, 0xFF0000); // make it red
}
PublishToDashboard(2, var_SensorValueToPublish, var_SensorActualValue);
//PublishToDashboard(2, var_SensorValue);
}
var_LastCurrentSensor2Read = millis();
}
}
//======================================================================================
void ModifyNeoPixel(int theNeoPixel, int theColor)
{
thePixelObject.setPixelColor(theNeoPixel,theColor);
thePixelObject.setBrightness(var_PixelBrightness);
thePixelObject.show(); // Sends the updated pixel info out to the neopixels
}
//====================================================================================
void PublishToDashboard(int theObjectNumber, int theValue, float theActualValue)
//void PublishToDashboard(int theObjectNumber, int theValue)
{
if(mqtt.Update())
{
// //Serial.println("mqtt.update is true");
//theCurrentSensorOneStateObject.publish(theSensor1);
//theCurrentSensorTwoStateObject.publish(theSensor2);
if (theObjectNumber==1)
{
// Particle.publish("Epilog1",String(theActualValue));
// theCurrentSensorOneStateObject.publish(theValue);
// theCurrentSensorOneActualValue.publish(theActualValue);
sensorOneState.publish(theValue);
sensorOneValue.publish(theActualValue);
}
else
{
if (theObjectNumber==2)
{
// theCurrentSensorTwoStateObject.publish(theValue);
// theCurrentSensorTwoActualValue.publish(theActualValue);
sensorTwoState.publish(theValue);
sensorTwoValue.publish(theActualValue);
}
}
}
}
//====================================================================================
// Function to connect and reconnect as necessary to the MQTT server.
void MQTT_connect()
{
int8_t ret;
// Stop if already connected.
if (mqtt.connected())
{
return;
}
Serial.print("Connecting to MQTT... ");
while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
Serial.println(mqtt.connectErrorString(ret));
Serial.println("Retrying MQTT connection in 5 seconds...");
mqtt.disconnect();
delay(5000); // wait 5 seconds
}
Serial.println("MQTT Connected!");
}
//===================================================================================
// Function to get Current reading from sensor
float getCurrent(int address, int i)
{
//int var_SensorIndex; // index for the arr_current array
//float arr_current[2];
// Start I2C Transmission
Wire.beginTransmission(Addr);
// Command header byte-1
Wire.write(0x92);
// Command header byte-2
Wire.write(0x6A);
// Command 1
Wire.write(0x01);
// Start Channel No.
Wire.write(i);
// End Channel No.
Wire.write(i);
// Reserved
Wire.write(0x00);
// Reserved
Wire.write(0x00);
// CheckSum
Wire.write((0x92 + 0x6A + 0x01 + i + i + 0x00 + 0x00) & 0xFF);
// Stop I2C Transmission
Wire.endTransmission();
delay(500);
// Request 3 bytes of data
Wire.requestFrom(Addr, 3);
// Read 3 bytes of data
// msb1, msb, lsb
byte msb1 = Wire.read();
byte msb = Wire.read();
byte lsb = Wire.read();
var_SensorActualValue = msb1<<16 | msb<<8 | lsb;
// Convert the data to ampere
var_SensorActualValue = var_SensorActualValue / 1000;
return var_SensorActualValue;
}
//===================================================================================
// Initialize current sensor
void currentInit()
{
// Start I2C transmission
Wire.beginTransmission(Addr);
// Command header byte-1
Wire.write(0x92);
// Command header byte-2
Wire.write(0x6A);
// Command 2 is used to read no of sensor type, Max current, No. of channel
Wire.write(0x02);
// Reserved
Wire.write(0x00);
// Reserved
Wire.write(0x00);
// Reserved
Wire.write(0x00);
// Reserved
Wire.write(0x00);
// CheckSum
Wire.write(0xFE);
// Stop I2C transmission
Wire.endTransmission();
}
Comments