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!
Islam Rostom
Published © GPL3+

Bitcoin Powered Electrical Outlet

A smart electrical outlet that allows customers to rent electricity by paying with Bitcoins.

IntermediateShowcase (no instructions)4,904
Bitcoin Powered Electrical Outlet

Things used in this project

Hardware components

Wemos D1 Mini
Espressif Wemos D1 Mini
×1
Micro SD Wemos D1 Mini Data Logger Shield
×1
current sensor ACS-712 Module
×1
Relay Module (Generic)
×1
Standard LCD - 16x2 White on Blue
Adafruit Standard LCD - 16x2 White on Blue
×1
I2C Serial Interface Board Module for LCD
×1
100-240V AC to 5V DC HLK-PM01
×1
Electrical outlet (Generic)
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Female Header 8 Position 1 Row (0.1")
Female Header 8 Position 1 Row (0.1")
×2
FR-4 Double Side Prototype PCB
×1
General Purpose Transistor NPN
General Purpose Transistor NPN
×1
3.3V voltage regulator (Generic)
×1
Resistor 10k ohm
Resistor 10k ohm
×2
Resistor 2.21k ohm
Resistor 2.21k ohm
×1
Resistor 221 ohm
Resistor 221 ohm
×1
Capacitor 10 µF
Capacitor 10 µF
×1
Capacitor 100 nF
Capacitor 100 nF
×1
SparkFun push button
×1
SparkFun screw terminal 2 pin
×1

Software apps and online services

Arduino IDE
Arduino IDE
Koyn Library

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Bitcoin powered electrical outlet schematic

Code

Bitcoin powered electrical outlet

Arduino
#include <LiquidCrystal_I2C.h>
#include <ESP8266WiFi.h>
#include <Wire.h>
#include <Koyn.h>

#define WIF_FORMAT_PRIVATE_KEY "YOUR WIF format private key"
#define SSID_name "SSID"
#define SSID_password "Password"
#define ButtonPin D4
#define sensorIn A0
#define RelayPin D3



//LCD Declaration
LiquidCrystal_I2C lcd(0x3F, 16, 2); // Declare LCD object
byte customChar1[] = {0x04, 0x04, 0x04, 0x04, 0x04, 0x1F, 0x0E, 0x04};  //LCD custom character 1
byte customChar2[] = {0x01, 0x02, 0x04, 0x0F, 0x02, 0x04, 0x08, 0x10};  //LCD custom character 2

volatile unsigned long last_interrupt_time = 0;
volatile bool buttonFlag = false;                                      //Flag used inside the ISR

bool startFlag = false;
bool LCD1 = false;
bool LCD2 = false;

unsigned long lastMillis = 0;
double KwH_Purchased = 0;                                            //Amount of electrical capacity the payee purchased
float Percentage = 0.0;
double AmpsRMS = 0;                                                  //RMS Amperage measured at any instance
double KwH = 0;                                                      //Variable for KWH tracking per session
double Kw = 0;                                                       //Measured Kilo Watts at any instance


/*Koyn Library declarations*/
BitcoinAddress myAddress(WIF_FORMAT_PRIVATE_KEY, KEY_WIF);            //Device address Private Key
BitcoinAddress Payee;                                                 //Payee Address empty shell

uint64_t RecievedPayment = 0;                                         //Recieved Payment from payee
uint64_t KWH_Price = 1700;                                            //price per KWH in Satoshis


void setup() {
  pinMode(RelayPin, OUTPUT);
  digitalWrite(RelayPin, LOW);

  Serial.begin(115200);

  // initialize the LCD
  lcd.begin();
  lcd.createChar(1, customChar1);
  lcd.createChar(2, customChar2);
  lcd.backlight();
  lcd.home();
  lcd.print("Bitcoin Outlet");
  lcd.setCursor(0, 1);
  lcd.print("Initializing..");

  //Connect to your Wifi network
  wifiConnect();

  //Koyn library initialization
  Koyn.begin();
  Koyn.onNewTransaction(&paymentCallback);

  lastMillis = millis();

}

void loop() {

  if (Koyn.isSynced()) {
    Koyn.trackAddress(&myAddress);
  }
  Koyn.run();

  if (startFlag) {

    getKWattage();

    if (millis() - lastMillis >= 600000) {

      KwH += ((Kw * 10) / 60);
      lastMillis = millis();
      Percentage = (KwH / KwH_Purchased) * 100;
    }

    lcd.clear();
    lcd.home();
    lcd.write(2);
    lcd.print(" Used: ");
    lcd.print(Percentage, 2);
    lcd.print("%");
    lcd.setCursor(0, 1);
    lcd.write(2);
    lcd.print(" bought: ");
    lcd.print(KwH_Purchased, 2);
    lcd.print("KWH");


    if (KwH >= KwH_Purchased) {

      detachInterrupt(ButtonPin);
      //turn off electricity
      digitalWrite(RelayPin, LOW);
      KwH = 0;
      KwH_Purchased = 0;
      lcd.clear();
      lcd.home();
      lcd.print("Session ended");
      lcd.setCursor(0, 1);
      lcd.print("Instructions ");
      lcd.write(1);
      startFlag = 0;
    }
  }
  if (buttonFlag) {
    lcd.clear();
    lcd.home();
    lcd.print("press again to");
    lcd.setCursor(0, 1);
    lcd.print("end this session");
    delay(6000);
    buttonFlag = false;
  }

}


void getKWattage()
{
  float VPP;
  int readValue;                                  //value read from the sensor
  int maxValue = 0;                               // store max value here
  int minValue = 1024;                            // store min value here
  int mVperAmp = 66;                              // use 100 for 20A Module and 66 for 30A Module
  double Voltage = 0;
  double VRMS = 0;

  uint32_t start_time = millis();
  while ((millis() - start_time) < 1000)          //sample for 1 Sec
  {
    readValue = analogRead(sensorIn);
    // see if you have a new maxValue
    if (readValue > maxValue)
    {
      /*record the maximum sensor value*/
      maxValue = readValue;
    }
    if (readValue < minValue)
    {
      /*record the maximum sensor value*/
      minValue = readValue;
    }
  }
  VPP = ((maxValue - minValue) * 3.3) / 1024.0;
  VRMS = (VPP / 2.0) * 0.707;
  AmpsRMS = (VRMS * 1000) / mVperAmp;
  Kw = (AmpsRMS * 220) / 1000;
}



void wifiConnect()
{
  if (WiFi.status() == WL_CONNECTED) {

    WiFi.disconnect();
  }
  if (WiFi.status() != WL_CONNECTED) {

    WiFi.mode(WIFI_STA);

    WiFi.begin(SSID_name, SSID_password);
  }
  Serial.print("Connecting");
  while (WiFi.status() != WL_CONNECTED) {
    for (int i = 0; i < 500; i++) {
      delay(1);
    }
    Serial.print(".");
  }
  lcd.clear();
  lcd.home();
  lcd.print("Follow the");
  lcd.setCursor(0, 1);
  lcd.print("Instructions ");
  lcd.write(1);

}





void paymentCallback(BitcoinTransaction tx) {

  if (tx.getConfirmations() == 0) {                                 //Number of confirmations that the transaction must have
    for (int j = 0; j < tx.getOutputsCount(); j++)
    {
      BitcoinAddress to;
      tx.getOutput(j, &to);
      if (to == myAddress) {
        RecievedPayment = tx.getOutputAmount(j);
        if (RecievedPayment >= KWH_Price) {                         //Check that the recieved payment is equal to or greater than the proce of one KWH
          tx.getInput(0, &Payee);                                   //Save the address of the payee in Payee bitcoin address shell
          KwH_Purchased = RecievedPayment / KWH_Price;              //Calculate the amount of KWH purchased
          /* Give access to electricity*/
          startFlag = true;
          digitalWrite(RelayPin, HIGH);
          attachInterrupt(ButtonPin, Button_Interrupt, FALLING);
        }
        break;
      }
    }
  }
}

void Button_Interrupt() {

  unsigned long interrupt_time = millis();
  // If interrupts come faster than 300ms, assume it's a bounce and ignore
  if (interrupt_time - last_interrupt_time > 300)
  {
    if (!buttonFlag) {
      buttonFlag = true;
    }
    else if (buttonFlag) {
      startFlag = false;
      buttonFlag = false;
      digitalWrite(RelayPin, LOW);
      uint64_t change = (KwH_Purchased - KwH) * KWH_Price;
      detachInterrupt(ButtonPin);
      lcd.clear();
      lcd.home();
      lcd.print("Follow the");
      lcd.setCursor(0, 1);
      lcd.print("Instructions ");
      lcd.write(1);
      Koyn.spend(&myAddress, &Payee, change , 0);
      KwH = 0;
      KwH_Purchased = 0;
    }
  }
  last_interrupt_time = interrupt_time;
}

Credits

Islam Rostom

Islam Rostom

1 project • 8 followers
Application engineer @Elkrem,Inc.

Comments