Making a Bridge Between Cloudflare and Nodemcu

4 minute read

image

Story

This is a project I did over weekends to try out something new and learn. The idea was to get the Cloudflare analytics data from the Cloudflare API and display the total amount of requests made to my site in an LCD using a Nodemcu.

My approach was to make a custom API that communicates with the Cloudflare API and cache the response so the Nodemcu can request without limiting any request and it was easy to parse the JSON data with PHP and give out the data only I wanted to the Nodemcu.

To write the API I used a PHP micro-framework called Flight. Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applications.

Here is a visualized concept:

image

NOTE: My drawings and stuff are lame :P

Things used

WEB:

NODEMCU:

  • ESP8266WiFi - Library to use the built-in wifi module.
  • ESP8266HTTPClient - Library to send HTTP request.
  • ArduinoJson - Library that lets you parse JSON.

Custom API

This API will contain only one route that returns all the analytics data as JSON.

route.php

 1/*
 2============================================
 3        Routes
 4============================================
 5*/
 6
 7use App\Controllers\AnalyticsController;
 8
 9$analytics = new AnalyticsController();
10
11Flight::route('/analytics', array($analytics, 'analytics'));

analytics routes use the analytics controller. analytics controller contains one method called analytics() that is responsible for getting the Cloudflare analytics report and cache it for 2mins inside the API and parse the JSON and give back HTTP response. Cloudflare Email and the Token is set in the environment file of the application.

AnalyticsController.php

 1<?php
 2namespace App\Controllers;
 3
 4use Jinas\Jsonify\Util;
 5use Phpfastcache\Helper\Psr16Adapter;
 6
 7class AnalyticsController
 8{
 9    public function analytics(){
10        //Response Utility
11        $response = new Util();
12
13        $defaultDriver = 'Files';
14        $Psr16Adapter = new Psr16Adapter($defaultDriver);
15
16        if (!$Psr16Adapter->has('cloudflaredata')) {
17            // Setter action
18            $client = new \Cloudflare\Api(getenv('CLOUDFLARE_EMAIL'), getenv('CLOUDFLARE_APIKEY'));
19
20            $analytic = new \Cloudflare\Zone\Analytics($client);
21            //First argument is the zone id in the cloudflare.
22            $array = json_decode(json_encode($analytic->dashboard('50515cfef4495e8bb32f79f6b28b1b54')), true);
23
24            $index = $array["result"]["timeseries"][6];
25            $Psr16Adapter->set('cloudflaredata', $index, 120); // 2 minutes before cache expire
26        } else {
27            // Getter action
28            $index = $Psr16Adapter->get('cloudflaredata');
29        }
30
31        $request = $index["requests"]["all"];
32        $bandwidth = $index["bandwidth"]["all"];
33        $pageviews = $index["pageviews"]["all"];
34        $uniques = $index["uniques"]["all"];
35
36
37        if (!array_key_exists("MV", $index["bandwidth"]["country"])) {
38            $country_bandwidth = null;
39        } else {
40            $country_bandwidth = $index["bandwidth"]["country"]["MV"];
41        }
42
43
44        $data = [
45            'total_request' => $request,
46            'total_bandwidth' => $bandwidth,
47            'total_pageviews' => $pageviews,
48            'total_unique_users' => $uniques,
49            'total_bandwidth_local' => $country_bandwidth
50        ];
51
52        echo $response->sendResponse($data, 'data retrieved successfully');
53    }
54}

Whoohoo!. So basically the API part is pretty much done. Now all I need to do is connect the Nodemcu to the API and display it in the LCD. The API is hosted on my local network using a raspberry pi in the same network the Nodemcu

Connecting everything together At this point, I am all set to send HTTP request to my API from the Nodemcu. Not so fast!.

Here is the Arduino codes I wrote for the Nodemcu to interface with my API.

getdata.ino

 1// Importing the libraries
 2#include <ESP8266WiFi.h>
 3#include <ESP8266HTTPClient.h>
 4#include <ArduinoJson.h>
 5
 6#define LEDRED D8
 7#define LEDYELLOW D7
 8#define LEDGREEN D6
 9
10const char* ssid = "";
11const char* password = "";
12
13void setup() {
14
15pinMode(LEDRED, OUTPUT);
16pinMode(LEDYELLOW, OUTPUT);
17pinMode(LEDGREEN, OUTPUT);
18
19Serial.begin(9600);
20WiFi.begin(ssid, password);
21
22while (WiFi.status() != WL_CONNECTED) {
23
24delay(1000);
25Serial.println("Connecting..");
26
27}
28
29
30}
31
32void loop() {
33
34if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status
35
36HTTPClient http;  //Declare an object of class HTTPClient
37
38http.begin("http://192.168.1.8:8000/analytics");  //Specify request destination
39int httpCode = http.GET();                                                                  //Send the request
40
41if (httpCode > 0) { //Check the returning code
42
43String payload = http.getString();   //Get the request response payload
44StaticJsonDocument<500> doc;
45
46DeserializationError error = deserializeJson(doc,payload);   //Parse messageif (error) {      //Check for errors in parsing
47    Serial.print("deserializeJson() failed with code ");
48    Serial.println(error.c_str());
49    delay(5000);
50    return;
51
52  }
53
54int totalrequest = doc["data"]["total_request"];
55
56  if(totalrequest >= 300)
57  {
58  digitalWrite(LEDRED, HIGH);
59  delay(400);
60  digitalWrite(LEDRED, LOW);
61  delay(400);
62  }
63
64  if(totalrequest < 300 && totalrequest >= 250)
65  {
66    digitalWrite(LEDYELLOW, HIGH);
67    delay(400);
68    digitalWrite(LEDYELLOW, LOW);
69    delay(400);
70  }
71
72  if(totalrequest < 250)
73  {
74    digitalWrite(LEDGREEN, HIGH);
75    delay(400);
76    digitalWrite(LEDGREEN, LOW);
77    delay(400);
78  }
79Serial.println(totalrequest);
80
81}
82
83http.end();   //Close connection
84
85}
86
87delay(10000);
88}

If you check the code you might have noticed there is leds in them. This is the point in the project I was stuck with an issue. Nodemcu gives out a voltage 3.3V but the LCD I am using needs 5V to operate. I forgot to check it before. So I tried to connect a 7 segment numeric LED but it also requires more digital pins in the Nodemcu 🤦‍♂️. But I found a way to solve this issue.

The solution is to connect an external power source to the components that need more power and let the Nodemcu do all the logical work. So for the time being connected 3 LED indicates the request limits. Green LED for Safe,Yellow LED for warning and a RED LED for danger if the request limit reaches a certain limit. Not much but it workss!!. Sometimes I don’t achieve the exact thing I want to do but failing, learning and trying it again is the point right??. Hope this is an interesting read. I just wanted to share my story of trying to implement this idea.