/***************************************************************************************************************************************************************************
IMPORT LIBRARY
****************************************************************************************************************************************************************************/
#include "FFT.h"
#include "FFT_signal.h"
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>
#include <WiFi.h>
#include <ArduinoJson.h>
#include "Base64.h"
#include "HTTPClient.h"
/***************************************************************************************************************************************************************************
DEKLARASI VARIABEL GLOBAL
****************************************************************************************************************************************************************************/
// Variabel ini berisikan SSID Wi-Fi default, akan dicoba pertama kali.
const char* default_wifi_ssid = "Asepsaori";
// Variabel ini berisikan password Wi-Fi default, akan dicoba pertama kali.
const char* default_wifi_pswd = "11112222";
// Variabel ini akan menyimpan SSID Wi-Fi yang di inputkan secara manual.
String wifi_ssid;
// Variabel ini akan menyimpan password Wi-Fi yang di inputkan secara manual.
String wifi_pswd;
// Variabel ini nantinya akan menjadi end point URL untuk HTTP-POST.
String post_addr;
// Variabel ini nantinya akan menjadi end point URL untuk HTTP-GET dalam inisialisasi session.
String init_addr;
// Variabel ini nantinya akan menyimpan SESSION_ID. Jadi setiap kali sensor dimulai ID akan selalu unik.
// ID ini akan membedakan antara sesi satu dengan sesi lainnya dalam database.
String session_id;
// Variabel ini menyimpan nomor (indeks) dari sampel data MPU-6050.
unsigned long data_sequencing;
// Variabel ini menyimpan data terakhir Frekuensi dari sensor MPU-6050.
float data_frequency;
// Variabel ini menyimpan data terakhir Magnitudo dari sensor MPU-6050.
float data_magnitude;
// Variabel ini menyimpan data terakhir Temperature (dalam derajat celcius) dari sensor MPU-6050.
float data_temperature;
// Variabel ini mendeklarasikan nilai waktu dalam milidetik, yaitu waktu delay dalam setiap loop. Default = 500ms.
int global_loop_delay;
// Variabel ini mendeklarasikan nilai Boolean, dimana:
// TRUE = hentikan sementara proses membaca sensor MPU-6050;
// FALSE = lanjutkan untuk membaca sensor MPU-6050;
// ~ Menghentikan sensor berguna jika sensor overheating saat proses berjalan. Default = FALSE;
bool global_stop_read;
// Variabel ini mendeklarasikan nilai Boolean (Default = FALSE), dimana:
// TRUE = hentikan upload data sensor ke web server;
// FALSE = lanjutkan upload data sensor ke web server;
bool global_stop_post;
// Variabel ini mendeklarasikan nilai Boolean (Default = FALSE), dimana:
// TRUE = jangan nyalakan BEEP jika sensor terjadi abnormal, atau ketika pahat dirasa sudah aus kritis;
// FALSE = nyalakan BEEP dalam kondisi tersebut.
// ~ BEEP hanya akan digunakan jika modul BEEP dipasang kedalam device!
bool global_stop_beep;
// Variabel ini mendeklarasikan nilai Boolean (Default = FALSE), dimana:
// TRUE = Jangan nyalakan LED (kedip-kedip) jika kondisi abnormal, atau LED (tetap menyala) dalam kondisi normal.
// FALSE = Nyalakan LED dalam kondisi tersebut.
// ~ LED hanya akan dinyalakan jika pin LED terpasang!
bool global_stop_blink;
// Variabel ini adalah kelas Adafruit_MPU6050, kelas ini mendukung untuk membaca data dari sensor MPU-6050.
Adafruit_MPU6050 MPU6050;
/***************************************************************************************************************************************************************************
PROSEDUR PROGRAM
****************************************************************************************************************************************************************************/
bool configure_session()
{
if(WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
http.begin(init_addr);
int http_status = http.GET();
if(http_status == 200)
{
session_id = http.getString();
Serial.print("> Session ID: ");
Serial.println(session_id);
return true;
}
else
{
Serial.print("!ERROR! Gagal mendapatkan session ID: Error ");
Serial.print(http_status);
Serial.print(" -> ");
Serial.println(http.getString());
return false;
}
}
else
{
Serial.println("!ERROR! Gagal mendapatkan session ID: Tidak ada koneksi internet!");
return false;
}
}
// Prosedur ini digunakan untuk setup variabel global kedalam masing masing nilai defaultnya.
// Prosedur ini juga digunakan untuk instalasi modul tambahan, seperti BEEP dan LEDs.
void configure_variables()
{
// konfigurasi variabel global saat booting.
// Variabel ini berisikan ID dari server (asepsaori17.com)
char raw_post[] = "aHR0cDovL2Z0bXVuamFuaS5uZXQvdmlicm8vcmV0cmlldmUucGhw";
int raw_size = sizeof(raw_post);
int plain_size = Base64.decodedLength(raw_post, raw_size);
char plain_post[plain_size];
Base64.decode(plain_post, raw_post, raw_size);
post_addr = String(plain_post);
char raw_init[] = "aHR0cDovL2Z0bXVuamFuaS5uZXQvdmlicm8vaW5pdGlhbGl6ZS5waHA=";
raw_size = sizeof(raw_init);
plain_size = Base64.decodedLength(raw_init, raw_size);
char plain_init[plain_size];
Base64.decode(plain_init, raw_init, raw_size);
init_addr = String(plain_init);
data_sequencing = 0;
data_frequency = 0.0;
data_magnitude = 0.0;
data_temperature = 0.0;
global_loop_delay = 100;
global_stop_post = false;
global_stop_read = false;
global_stop_beep = false;
global_stop_blink = false;
}
// Fungsi ini akan menanyakan SSID dan Password untuk Wi-Fi jika default WiFI profile gagal terhubung, sehingga SSID dan Password bisa dinamis.
// Koneksi yang digunakan ialah Serial dengan baud rate 115200 dan COM{N} dimana {N} ialah port yang digunakan.
bool configure_wifi()
{
Serial.print("~Inputkan WiFi SSID: ");
while (!Serial.available());
String ssid = Serial.readString();
ssid.trim();
char chr_ssid[ssid.length() + 1];
ssid.toCharArray(chr_ssid, ssid.length() + 1);
Serial.println(chr_ssid);
if (!ssid.length())
{
Serial.println("!ERROR! WiFi SSID tidak boleh kosong.");
return false;
}
Serial.print("~Inputkan WiFi Password: ");
while (!Serial.available());
String password = Serial.readString();
password.trim();
char chr_pswd[password.length() + 1];
password.toCharArray(chr_pswd, password.length() + 1);
Serial.println(chr_pswd);
int attempt = 0;
if (!password.length())
{
Serial.print("> Menghubungkan WiFi tanpa password");
WiFi.begin(chr_ssid);
}
else
{
Serial.print("> Menghubungkan WiFi");
WiFi.begin(chr_ssid, chr_pswd);
}
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print('.');
if (++attempt > 15)
{
Serial.println();
Serial.println("> Gagal menghubungkan WiFi, cek kembali SSID atau password.");
return false;
}
}
Serial.println("> Terhubung ke WiFi");
Serial.print("> Alamat IP: ");
Serial.println(WiFi.localIP());
wifi_ssid = ssid;
if (password.length())
{
wifi_pswd = password;
}
else
{
wifi_pswd = "";
}
return true;
}
// Prosedur ini akan meminta user untuk memasukan SSID dan Password untuk Wi-Fi sampai limit yang ditentukan.
// Jika sudah mencapai limit namun tidak terhubung kedalam Wi-Fi maka device akan error!
void configure_wifi_run(int limit)
{
while (!configure_wifi())
{
if (--limit < 1)
{
Serial.println("!Device Error! No WiFi connection. Press RESET the device.");
while (1);
}
}
}
// Fungsi ini akan mencoba untuk menghubungkan Wi-Fi dengan default profile, yaitu menggunakan data dari variabel ini:
// default_wifi_ssid (const char*) = SSID (Service Set Identifier), atau dikenal dengan nama WiFi.
// default_wifi_pswd (const char*) = Password WiFi (jika WiFi tidak memiliki password, maka kosong).
// Jika dimana percobaan gagal, maka akan meminta user untuk memasukan SSID dan Password secara manual melalui Serial.
bool configure_wifi_default()
{
Serial.print("> Menghubungkan ke WiFi (default profile) ");
WiFi.begin(default_wifi_ssid, default_wifi_pswd);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
if (++attempts > 20)
{
WiFi.disconnect();
Serial.println("> Gagal menghubungkan WiFi, mencoba mode manual.");
return false;
}
}
Serial.println("> Terhubung WiFi menggunakan default profile.");
Serial.print("> Alamat IP: ");
Serial.println(WiFi.localIP());
return true;
}
// Prosedur ini digunakan untuk menghubungkan sensor MPU-6050 saat tahapan booting.
// Jika gagal menghubungkan, maka device akan ERROR. Tekan RESET untuk mencoba kembali.
void configure_mpu6050()
{
// Pada bagian ini, kita akan mencoba untuk menghubungkan sensor MPU-6050
if (!MPU6050.begin())
{
// MPU6050 tampaknya tidak terbaca atau tidak terhubung pada kondisi ini.
Serial.println("!ERROR! Tidak dapat mencari modul MPU6050! Pastikan modul terhubung dan tekan RESET untuk mengulang kembali.");
while (1)
{
delay(50);
}
}
MPU6050.setAccelerometerRange(MPU6050_RANGE_8_G);
MPU6050.setGyroRange(MPU6050_RANGE_500_DEG);
MPU6050.setFilterBandwidth(MPU6050_BAND_21_HZ);
Serial.println("> Modul MPU6050 telah terhubung!");
}
// Prosedur untuk memproses perintah.
void process_command()
{
if (Serial.available())
{
String command = Serial.readString();
command.trim();
if (!command.length()) return;
command.toUpperCase();
if (command == "#READ:STOP")
{
global_stop_read = true;
MPU6050.enableSleep(true);
Serial.println("> Modul MPU-6050 berhenti membaca.");
}
else if (command == "#READ:START")
{
global_stop_read = false;
MPU6050.enableSleep(false);
Serial.println("> Modul MPU-6050 mulai membaca.");
}
else if (command == "#POST:STOP")
{
global_stop_post = true;
Serial.println("> Uploading data dihentikan.");
}
else if (command == "#POST:START")
{
global_stop_post = false;
Serial.println("> Uploading data dilanjutkan.");
}
}
}
// Prosedur inisialisasi device (setup), akan dipanggil satu kali oleh device, yaitu ketika proses booting.
void setup()
{
// buka komunikasi serial dengan baud-rate 115200
Serial.begin(115200);
// kita akan coba hubungkan modul (sensor) MPU-6050
configure_mpu6050();
// Pada bagian ini, kita akan mencoba untuk menghubungkan device kedalam jaringan Wi-Fi.
// Jika gagal menggunakan profile Wi-Fi default, maka gunakan mode manual.
if (!configure_wifi_default()) configure_wifi_run(10);
// terakhir kita akan konfigurasikan variabel global.
configure_variables();
if(!configure_session())
{
Serial.println("> Perbaiki error terlebih dahulu, dan RESET setelah diperbaiki.");
while(1);
}
}
void upload()
{
if (!global_stop_post && WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
http.begin(post_addr.c_str());
http.addHeader("Content-Type", "application/json");
StaticJsonDocument<200> doc;
doc["g"] = session_id;
doc["n"] = data_sequencing;
doc["f"] = data_frequency;
doc["m"] = data_magnitude;
doc["t"] = data_temperature;
char json[255];
serializeJson(doc, json);
int http_status = http.POST(json);
if(http_status == 200)
{
Serial.println("\tUploaded");
}
else
{
Serial.print("\tERROR ");
Serial.print(http_status);
String payload = http.getString();
payload.trim();
if(payload.length() > 0)
{
Serial.print(": ");
Serial.println(payload);
}
else
{
Serial.println();
}
}
}
}
void loop()
{
process_command();
if (global_stop_read) return;
fft_config_t *real_fft_plan = fft_init(FFT_N, FFT_REAL, FFT_FORWARD, fft_input, fft_output);
sensors_event_t a, g, temp;
long int t1, t2;
float total_time, max_magnitude = 0, magnitude = 0, frequency = 0;
t1 = micros();
for ( int i = 0; i < FFT_N; i++ ) {
MPU6050.getEvent(&a, &g, &temp);
fft_signal[i] = a.acceleration.x * 10000.0;
}
t2 = micros();
total_time = (t2 - t1) * 1.0 / 1000000;
for (int k = 0 ; k < FFT_N ; real_fft_plan->input[k] = (float)fft_signal[k++]);
t1 = micros();
fft_execute(real_fft_plan);
for (int k = 1 ; k < real_fft_plan->size / 2 ; k++)
{
float mag = sqrt(pow(real_fft_plan->output[2 * k], 2) + pow(real_fft_plan->output[2 * k + 1], 2)) / 1, freq = k * 1.0 / total_time;
if (mag > max_magnitude)
{
max_magnitude = mag;
frequency = freq;
}
}
t2 = micros();
magnitude = (max_magnitude / 10000) * 2 / FFT_N;
data_sequencing++;
data_frequency = frequency;
data_magnitude = magnitude;
data_temperature = temp.temperature;
Serial.print(data_sequencing);
Serial.print("\t");
Serial.print(data_frequency);
Serial.print("\t");
Serial.print(data_magnitude);
Serial.print("\t");
Serial.print(data_temperature);
Serial.print("\u00b0");
if (!global_stop_post) upload();
else Serial.println();
if (global_loop_delay > 0) delay(global_loop_delay);
}