Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Dhrumil Makadia
Created March 31, 2024

Smart Lights with Energy Consumption

Revolutionize lighting efficiency with smart lights that track energy consumption, saving costs while promoting sustainability

13
Smart Lights with Energy Consumption

Things used in this project

Hardware components

AVR IoT Mini Cellular Board
Microchip AVR IoT Mini Cellular Board
×1
SparkFun Low Current Sensor Breakout - ACS712
SparkFun Low Current Sensor Breakout - ACS712
×1
4-CHANNEL RELAY CONTROLLER FOR I2C
ControlEverything.com 4-CHANNEL RELAY CONTROLLER FOR I2C
×1

Software apps and online services

MQTT
MQTT
Arduino IDE
Arduino IDE
time.nist.gov

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Solder Paste, Rework
Solder Paste, Rework

Story

Read more

Schematics

Circuit Diagrams

Code

Arduino Code

Arduino
// #include <Arduino.h>

#include <ecc608.h>
#include <led_ctrl.h>
#include <log.h>
#include <lte.h>
#include <mqtt_client.h>
#include "ACS712.h"
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>

#define CURRENT_SENSOR_PIN A0
#define VOLTAGE_SENSOR_PIN A1
#define RELAY_PIN 8
#define RESISTOR_VALUE 10000.0  // Resistance value of voltage divider resistor in Ohms
// #define ACS712_SENSITIVITY 66.0 // Sensitivity of ACS712 sensor (mV/A for ACS712-20A)

#define MQTT_SUB_TOPIC "Control"
#define MQTT_SUB_COMMON_TOPIC "Common"
#define MQTT_PUB_TOPIC "publish"

#define MQTT_THING_NAME "12345"
#define MQTT_BROKER "tailor.cloudmqtt.com"
#define MQTT_PORT 13582
#define MQTT_USE_TLS false
#define MQTT_KEEPALIVE 60
#define MQTT_USE_ECC false
#define MOSQUITTO_USERNAME "ffbqwwfa"
#define MOSQUITTO_PASSWORD "abcdefgh"
#define MAX_MESSAGE_LENGTH 256

char message_buff[MAX_MESSAGE_LENGTH];  // Define the message buffer

// ACS712 sensor(ACS712_20A, CURRENT_SENSOR_PIN); // Create an ACS712 object for a 20A sensor
// 5.0 volt with a max ADC value of 1023 steps
ACS712 ACS(A0, 5.0, 1023, 100);

static volatile bool connecteded_to_network = false;
static volatile bool connected_to_broker = false;

// Enter a MAC address for your controller below.
byte mac[] = { 0xD0, 0x12, 0xBE, 0x6F, 0x1E, 0x9D };
// Set the IP address of the Arduino
IPAddress ip(192, 168, 1, 177);

// NTP Servers:
IPAddress timeServer(132, 163, 96, 1);  // time.nist.gov

// NTP time stamp is in the first 48 bytes of the message
const unsigned int NTP_PACKET_SIZE = 48;
byte packetBuffer[NTP_PACKET_SIZE];

// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;


unsigned long lastSyncTime = 0; // Last time synced with NTP server
const unsigned long syncInterval = 30000; // Sync interval in milliseconds (30 seconds)


void disconnectedFromBroker(void) {
  connected_to_broker = false;
}

void disconnectedFromNetwork(void) {
  connecteded_to_network = false;
}

static bool connectLTE() {
  // Connect with a maximum timeout value of 30 000 ms, if the connection is
  // not up and running within 30 seconds, abort and retry later
  if (!Lte.begin(30000)) {
    return false;
  } else {
    return true;
  }
}

static bool connectMqtt() {

  // Attempt to connect to the broker
  if (!MqttClient.begin(MQTT_THING_NAME,
                        MQTT_BROKER,
                        MQTT_PORT,
                        MQTT_USE_TLS,
                        MQTT_KEEPALIVE,
                        MQTT_USE_ECC,
                        MOSQUITTO_USERNAME,
                        MOSQUITTO_PASSWORD)) {
    return false;
  }

  return true;
}

void onMqttMessage(char* topic, char* payload, unsigned int length) {
  Serial.print("Message arrived on topic: ");
  Serial.println(topic);

  // Clear the buffer
  memset(message_buff, 0, sizeof(message_buff));

  // Copy payload into message_buff
  for (unsigned int i = 0; i < length && i < MAX_MESSAGE_LENGTH - 1; i++) {
    message_buff[i] = payload[i];
  }

  // Ensure null-termination of the string
  message_buff[length < MAX_MESSAGE_LENGTH ? length : MAX_MESSAGE_LENGTH - 1] = '\0';

  Serial.print("Message received: ");
  Serial.println(message_buff);

  if (strcmp(message_buff, "meterdata") == 0) {
    // if(message_buff == "meterdata"){
    // Read current in mA
    float current = ACS.mA_AC();

    // Read voltage using voltage divider
    int rawVoltage = analogRead(VOLTAGE_SENSOR_PIN);
    float voltage = (rawVoltage / 1023.0) * 5.0;    // Convert raw value to voltage
    voltage *= (RESISTOR_VALUE + 1000.0) / 1000.0;  // Adjust voltage based on voltage divider ratio

    // Calculate power
    float power = voltage * (current / 1000.0);  // Power in Watts

    // Calculate energy consumed over time (in Wh)
    float energy = power / 3600.0;  // Assuming 1-hour resolution (3600 seconds)

    // Print readings
    Serial.print("Current (mA): ");
    Serial.print(current);
    Serial.print("\tVoltage (V): ");
    Serial.print(voltage);
    Serial.print("\tPower (W): ");
    Serial.print(power);
    Serial.print("\tEnergy (Wh): ");
    Serial.println(energy);

    String message_to_publish = String(String(current) + "||" + String(voltage) + "||" + String(power) + "||" + String(energy) );

    bool publishedSuccessfully =
        MqttClient.publish(MQTT_PUB_TOPIC, message_to_publish.c_str());

    if (publishedSuccessfully) {
        Log.infof(F("Published message: %s\r\n"),
                  message_to_publish.c_str());
    } else {
        Log.error(F("Failed to publish"));
    }
  }

  if (strcmp(topic, "Control") == 0) {
    if (strcmp(message_buff, "on") == 0) {
      digitalWrite(RELAY_PIN, HIGH);
    } else if (strcmp(message_buff, "off") == 0) {
      digitalWrite(RELAY_PIN, LOW);
    }
  }
}

void setup() {
  Log.begin(115200);
  Ethernet.begin(mac, ip);
  LedCtrl.begin();
  LedCtrl.startupCycle();

  pinMode(RELAY_PIN, OUTPUT);
  // Initialize UDP
  Udp.begin(8888);
  Log.info(F("Starting MQTT with username and password example"));

  // Establish LTE connection
  if (!Lte.begin()) {
    Log.error(F("Failed to connect to operator"));
    // Halt here
    while (1) {}
  }

  // Attempt to connect to the broker
  if (MqttClient.begin(MQTT_THING_NAME,
                       MQTT_BROKER,
                       MQTT_PORT,
                       MQTT_USE_TLS,
                       MQTT_KEEPALIVE,
                       MQTT_USE_ECC,
                       MOSQUITTO_USERNAME,
                       MOSQUITTO_PASSWORD)) {

    if (MqttClient.subscribe(MQTT_SUB_TOPIC)) {
      Log.infof(F("Subscribed to %s\r\n"), MQTT_SUB_TOPIC);
    } else {
      Log.error(F("Failed to subscribe to topic"));
      // Halt here
      while (1) {}
    }
    if (MqttClient.subscribe(MQTT_SUB_COMMON_TOPIC)) {
      Log.infof(F("Subscribed to %s\r\n"), MQTT_SUB_COMMON_TOPIC);
    } else {
      Log.error(F("Failed to subscribe to topic"));
      // Halt here
      while (1) {}
    }
  } else {
    // Halt here
    while (1) {}
  }
  // Log.info("Closing MQTT connection");
  // MqttClient.end();

  Lte.onDisconnect(disconnectedFromNetwork);

  MqttClient.onDisconnect(disconnectedFromBroker);
}

void loop() {
  if (!connecteded_to_network) {
    Log.info(F("Not connected to the network. Attempting to connect!"));
    if (connectLTE()) {
      connecteded_to_network = true;
    }
  }

  if (!connected_to_broker && connecteded_to_network) {
    Log.info(F("Not connected to broker. Attempting to connect!"));

    if (connectMqtt()) {
      connected_to_broker = true;
    }
  }

  MqttClient.readMessage(MQTT_SUB_TOPIC);
  MqttClient.readMessage(MQTT_SUB_COMMON_TOPIC);

  unsigned long currentTime = millis();

  // Check if it's time to sync with the NTP server
  if (currentTime - lastSyncTime >= syncInterval || lastSyncTime == 0) {
    // Sync with NTP server
    sendNTPpacket(timeServer);
    lastSyncTime = currentTime;
  }

  // Check if data is available to be read
  if (Udp.parsePacket()) {
    Serial.println("Received NTP Response");

    // Read packet into the buffer
    Udp.read(packetBuffer, NTP_PACKET_SIZE);

    // Extract the timestamp
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    unsigned long secsSince1900 = highWord << 16 | lowWord;

    // Subtract 70 years (Unix epoch starts on Jan 1, 1970)
    const unsigned long seventyYears = 2208988800UL;
    unsigned long epoch = secsSince1900 - seventyYears;

    // Print the time
    Serial.print("Unix Epoch Time: ");
    Serial.println(epoch);
    // Get the current hour
    int currentHour = (epoch % 86400L) / 3600; // 86400 seconds in a day, 3600 seconds in an hour

    // Check if it's 6 PM
    if (currentHour == 18) { // 6 PM
      digitalWrite(RELAY_PIN, HIGH); 
    } else {
      digitalWrite(RELAY_PIN, LOW);
    }

    if (currentHour == 6) { // 6 AM
      digitalWrite(RELAY_PIN, LOW);
    } else {
      digitalWrite(RELAY_PIN, HIGH);
    }
  }
}


// Send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address) {
  // Set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);

  // Initialize values needed to form NTP request
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  packetBuffer[12] = 49;
  packetBuffer[13] = 0x4E;
  packetBuffer[14] = 49;
  packetBuffer[15] = 52;

  // Send request
  Udp.beginPacket(address, 123); // NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
}

Credits

Dhrumil Makadia

Dhrumil Makadia

40 projects • 43 followers

Comments