Bitcoin and blockchain technology opened a whole lot of opportunities for paying for goods and services but the catch was that it wasn't fully automated because it lacked easy hardware integration support. However, it got easier with the release of the Koyn library. In short, the Koyn library allows your hardware like Arduino compatible boards to receive and send Bitcoin payments.
One application model for Bitcoin that interests me is pay per service, and one of many applications I decided to get my hands dirty on is renting electricity. So, I started building an electrical outlet that is powered by Bitcoin.
DisclaimerThis project works on HIGH AC voltage which is quite dangerous. Your safety is your own responsibility, including proper use of equipment and safety gear, and determining whether you have adequate skill and experience. You must accept that you and you alone are responsible for your safety and the safety of others if you choose to build this project.
Brainstorming & SketchingAfter deciding to get my hands dirty, it was time to sit down and write down how the device would work and what the sketch would look like.
The device will work in this process:
- Power up
- Connect to Internet and sync to the Bitcoin network
- Listen for incoming transactions for its Bitcoin address
- If payment is received, calculate the capacity of electrical energy purchased
- Give access to electrical energy through the device's outlets
- Pressing a push button twice will end the current session and return the change (if found) to the payee's address
- After consuming, the purchased quota the device will end the session
The device will also include a 16 x 2 LCD to show its status.
The initial sketch of the device is shown below.
And the final sketch:
The first step was to choose a suitable microcontroller for this project.
Wemos D1 mini and SD card shield
I chose the Wemos D1 mini board, which is a mini WiFi board based on ESP8266EX. It is also supported by the Koyn library. To use the Koyn library, an SD card is required to save the blockchain headers and sync with the Bitcoin network. I used the data logger shield for Wemos with 8 GB micro SD card.
Note: You can use any micro SD card that has at least 1 GB of free space. for more info about the hardware requirements, you can check the Koyn library GitHub repository.
Next step was to find out how electrical consumption is measured and how to measure the electrical energy that the user will consume during his or her session.
Electricity is measured in KWH (Kilowatt-hour), which is a measure of the rate of electrical energy used multiplied by the length of time used.
To measure KWH, I used a current sensor module that measures the Amperage count that will be consumed at a time which is ACS-712.
ACS712 Current Sensor
ACS712 current sensors are designed to easily be used with any microcontroller with compelling features like:
- Full scale values of 5A, 20A, and 30A
- Output analog voltage with a scale factor of mV per Amp
- This sensor module requires no extra hardware to use except for a couple resistors to map the output from 5V to 3.3V as shown below
Measuring AC current is not straightforward as measuring DC current. For more information about how to measure AC current using ACS-712, you can check the further reading section.
The third step was to collect the necessary hardware to add controllability to the device.
Relay Module
A generic 5V relay module to switch electricity on/off to the outlet. To control this module using the Wemos board, which is a 3.3V board, I used a transistor as a switch to map the 3.3V volt to 5V. You can basically use any MOSFET or BJT to do that, but in my case I used BC547 which is NPN BJT.
AC to DC Module HLK-PM01
HLK-PM01 is a compact module that converts 100-240V AC to 5V DC. It is used to power up the components in this project like the relay module, current sensor and used with a 3.3 V regulator to power up the Wemos D1 mini board.
16 x 2 LCD with Serial I2C LCD Daughter Board Module
The LCD is going to show the device status and info relevant to the user like how much KWH he or she paid for, and how much KWH is left to use in percentage.
Since a generic 16 x 2 LCD will require a number of wires to control more than the Wemos support, I used a Serial I2C LCD daughter board module that enables you to control the LCD using the I2C protocol which is supported by the Wemos board.
Push Button
The push button gives the user the ability to end his session at any time and if there is a remaining quota the device will send back to the address he used to pay for the service an amount equals to the remaining quota in Satoshi.
Extra Hardware
I used a double layer hole PCB to connect everything together with some female headers and jumpers.
The final look of the ready hole PCB with the components soldered on it looked like this in my setup.
In the schematics section, you will find the complete connection of the hardware parts used in this project.
CodeThe code may look long but it's simple and pretty straightforward, am going to break it out to section and explain each section for ease of understanding.
Koyn library
- Declarations
BitcoinAddress myAddress(WIF_FORMAT_PRIVATE_KEY, KEY_WIF);//Device address Private Key
BitcoinAddress Payee; //Payee Address empty shell
I create two bitcoin address shells using BitcoinAddress class, The first one contains the private key of the device to allow spending/receiving of bitcoins and the second one is an empty shell to store the bitcoin address of the payee later.
- Setup
Koyn.begin();
Koyn.onNewTransaction(&paymentCallback);
Koyn.begin();
function starts the library by checking that the SD card is mounted, connecting to the servers and syncing with the network.
Koyn.onNewTransaction(&paymentCallback);
is a call back function that executes the paymentCallback function when a transaction is received to one of the addresses that the koyn library is tracking.
- Callback Function
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;
}
}
}
}
The callback function retrieves the received amount by the device's address in the transaction then it checks if the received amount is equal to or greater than the minimum amount which is the price of one KWH and if this condition is achieved it calculates how much electrical power the payee have purchased. Then gives access to electricity to the outlet by toggling the state of the relay.
- Main Loop
if (Koyn.isSynced()) {
Koyn.trackAddress(&myAddress);
}
Koyn.run();
run();
function is as explained by the wiki
The magical function where everything happens from syncing, verifying, checking connectivity and managing data.
Koyn class checks if the device is synced with the network through Koyn.isSynced();
function and if it is synced it tracks the myAddress address.
Calculating KWH
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;
}
getKWattage();
function runs in the main loop - when the startFlag
is set to true - calculates the amount of electrical energy that is being consumed at the time it is called.
More about the logic behind this can be found in the further reading section.
Main Loop
if (startFlag) {
getKWattage();
if (millis() - lastMillis >= 600000) {
KwH += ((Kw * 10) / 60);
lastMillis = millis();
Serial.println(" KWH Updated!");
Percentage = (KwH / KwH_Purchased) * 100;
}
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;
}
}
When the device receives a valid payment and starts supplying electrical power it calculates the Kilo watts being consumed and every 10 minutes updates the KWH variable.
Interrupt Service routine
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;
}
I attached the push button to an ISR to avoid the instance when the user pushes the button but the device is being synced with the network in Koyn.run();
function. Also, the ISR includes a software solution for the push button bouncing problem.
The rest is pretty straightforward, wifiConnect();
connects the Wemos board to your WIFI network and LiquidCrystal_I2C lcd(0x3F, 16, 2);
declares an LCD object that is controlled using IIC protocol.
Full code is attached in the attachments.
EnclosureI used foam sheets to build the enclosure as it is easy to cut and form. For the artistic side of my team, we used vinyl stickers to add a nice look to the enclosure.
Comments