#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FS.h>
#include <WebSocketsServer.h>
const char *ssid = "Kkrbt_mobile";
const char *password = "Kkrbt2024";
typedef String str;
Adafruit_MPU6050 mpu;
ESP8266WebServer server(80);
WebSocketsServer webSocket(81);
const char *dataFilePath = "/data.csv";
const int maxDataPoints = 30;
const int maxStoredDataPoints = 5000;
const int rangeDataY = 200;
float roll, pitch, yaw;
float rollData[maxDataPoints];
float pitchData[maxDataPoints];
float yawData[maxDataPoints];
unsigned long timestamps[maxDataPoints];
unsigned long previousMillis = 0, lastSaveTime = 0;
const long interval = 100;
bool paused = false;
// ... (function declarations)
void setup() {
Serial.begin(115200);
Wire.begin();
if (!SPIFFS.begin()) {
Serial.println("Failed to mount file system");
return;
}
while (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
delay(500);
}
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.println("Connecting to WiFi...");
}
Serial.println("Connected to WiFi");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Serial.println("Clearing data...");
File dataFile = SPIFFS.open(dataFilePath, "w"); // Open the file in write mode
if (dataFile) {
dataFile.close();
Serial.println("Data cleared successfully!");
} else {
Serial.println("Error: Unable to open file for clearing data!");
}
// Load existing data from file
loadStoredData();
// Setup route untuk mengambil data dan menampilkan grafik dan tabel
server.on("/download", HTTP_GET, handleDownload);
server.on("/", HTTP_GET, []() {
// Halaman HTML dan JavaScript untuk menampilkan grafik garis
String page = "<html><head>";
page += "<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>";
page += "<style> button { margin-top: 10px; }</style>"; // Menambahkan gaya untuk memindahkan tombol ke atas
page += "<div id='chart'></div>";
page += "<button onclick='togglePause()'>Pause</button>"; // Tombol Pause
page += "<div id='timestamp'></div>"; // Div untuk menampilkan timestamp
page += "<table border='1'>";
page += " <tr><th>Timestamp</th><th>Roll</th><th>Pitch</th><th>Yaw</th></tr>";
page += " <tbody id='dataTable'></tbody>";
page += "</table>";
page += "<button onclick='downloadData()'>Download Data</button>";
page += "<script>";
page += " var paused = false;";
page += " var chartDiv = document.getElementById('chart');";
page += " var timestampDiv = document.getElementById('timestamp');";
page += " var tableBody = document.getElementById('dataTable');";
// page += " var traceRoll = { x: [], y: [], type: 'line', name: 'Roll', line: {color: 'red'} };";
// page += " var tracePitch = { x: [], y: [], type: 'line', name: 'Pitch', line: {color: 'green'} };";
// page += " var traceYaw = { x: [], y: [], type: 'line', name: 'Yaw', line: {color: 'blue'} };";
page += " var traceRoll = { x: [], y: [], type: 'scatter', mode: 'lines+markers', name: 'Roll', line: {color: 'red', shape: 'spline'} };";
page += " var tracePitch = { x: [], y: [], type: 'scatter', mode: 'lines+markers', name: 'Pitch', line: {color: 'green', shape: 'spline'} };";
page += " var traceYaw = { x: [], y: [], type: 'scatter', mode: 'lines+markers', name: 'Yaw', line: {color: 'blue', shape: 'spline'} };";
page += " var layout = { title: 'Roll, Pitch, Yaw Over Time', xaxis: {title: 'Time'}, yaxis: {title: 'Angle (degrees)', range: ["+ str(-rangeDataY) + "," + str(rangeDataY) +"], tick0: 0, dtick: 20} };";
page += " Plotly.plot(chartDiv, [traceRoll, tracePitch, traceYaw], layout);";
page += " function updateChart(timestamps, roll, pitch, yaw) {";
page += " if (!paused) {";
page += " traceRoll.x = timestamps.map(formatTimestamp);";
page += " traceRoll.y = roll;";
page += " tracePitch.x = timestamps.map(formatTimestamp);";
page += " tracePitch.y = pitch;";
page += " traceYaw.x = timestamps.map(formatTimestamp);";
page += " traceYaw.y = yaw;";
page += " Plotly.update(chartDiv, [traceRoll, tracePitch, traceYaw], layout);";
page += " timestampDiv.innerHTML = 'Last Update: ' + formatTimestamp(timestamps[timestamps.length - 1]);";
page += " }";
page += " }";
page += " function updateTable(timestamps, roll, pitch, yaw) {";
page += " if (!paused) {";
page += " if (tableBody.rows.length >= " + String(maxDataPoints) + ") {";
page += " tableBody.deleteRow(0);";
page += " }";
page += " var newRow = tableBody.insertRow(-1);";
page += " var cell1 = newRow.insertCell(0);";
page += " var cell2 = newRow.insertCell(1);";
page += " var cell3 = newRow.insertCell(2);";
page += " var cell4 = newRow.insertCell(3);";
page += " cell1.innerHTML = formatTimestamp(timestamps[timestamps.length - 1]);";
page += " cell2.innerHTML = roll[roll.length - 1];";
page += " cell3.innerHTML = pitch[pitch.length - 1];";
page += " cell4.innerHTML = yaw[yaw.length - 1];";
page += " }";
page += " }";
page += " function formatTimestamp(timestamp) {";
page += " var date = new Date(timestamp);";
page += " var minutes = date.getMinutes();";
page += " var seconds = date.getSeconds();";
page += " var milliseconds = date.getMilliseconds();";
page += " return minutes + ':' + (seconds < 10 ? '0' : '') + seconds + ':' + (milliseconds < 10 ? '00' : (milliseconds < 100 ? '0' : '')) + milliseconds;";
page += " }";
page += " function togglePause() {";
page += " paused = !paused;";
page += " }";
page += " var ws = new WebSocket('ws://' + location.hostname + ':81/');";
page += " ws.onmessage = function(event) { var jsonData = JSON.parse(event.data); updateChart(jsonData.timestamps, jsonData.roll, jsonData.pitch, jsonData.yaw); updateTable(jsonData.timestamps, jsonData.roll, jsonData.pitch, jsonData.yaw); };";
page += " function downloadData() {";
page += " var xhr = new XMLHttpRequest();";
page += " xhr.open('GET', '/download', true);";
page += " xhr.responseType = 'blob';";
page += " xhr.onload = function() {";
page += " var a = document.createElement('a');";
page += " a.href = window.URL.createObjectURL(xhr.response);";
page += " a.download = 'data.csv';";
page += " a.style.display = 'none';";
page += " document.body.appendChild(a);";
page += " a.click();";
page += " document.body.removeChild(a);";
page += " };";
page += " xhr.send();";
page += " }";
page += "</script></body></html>";
server.send(200, "text/html", page);
});
server.begin();
webSocket.begin();
webSocket.onEvent(handleWebSocket);
}
void loop() {
server.handleClient();
webSocket.loop();
unsigned long currentMillis = millis();
// Read data from MPU6050
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
roll = atan2(a.acceleration.y, a.acceleration.z) * 180 / PI;
pitch = atan2(-a.acceleration.x, sqrt(a.acceleration.y * a.acceleration.y + a.acceleration.z * a.acceleration.z)) * 180 / PI;
yaw = g.gyro.z * 180 / PI;
sendWebSocketData();
// Save data to file every 1 second
if (currentMillis - lastSaveTime >= 1000) {
updateDataHistory(currentMillis, roll, pitch, yaw);
saveDataToFile();
lastSaveTime = currentMillis;
}
}
}
void saveDataToFile() {
Serial.println("Saving data to file...");
File dataFile = SPIFFS.open(dataFilePath, "a"); // Open the file in append mode
if (dataFile) {
if (dataFile.size() > 0) {
dataFile.println(); // Add a newline before appending new data, if the file is not empty
}
for (int i = 0; i < maxDataPoints; i++) {
if (timestamps[i] != 0) { // Only save valid data
dataFile.print(String(timestamps[i]) + "," + String(rollData[i]) + "," + String(pitchData[i]) + "," + String(yawData[i]));
if (i < maxDataPoints - 1 && timestamps[i + 1] != 0) {
dataFile.println(); // Add a newline if there is more data
}
}
}
dataFile.close();
Serial.println("Data saved successfully!");
} else {
Serial.println("Error: Unable to open file for writing!");
}
}
void handleDownload() {
File dataFile = SPIFFS.open(dataFilePath, "r");
if (dataFile) {
Serial.println("Sending CSV data for download...");
server.sendHeader("Content-Disposition", "attachment;filename=data.csv");
server.streamFile(dataFile, "text/csv");
dataFile.close();
} else {
Serial.println("Error: File not found!");
server.send(404, "text/plain", "File not found");
}
}
void loadStoredData() {
File dataFile = SPIFFS.open(dataFilePath, "r");
if (dataFile) {
int count = 0;
while (dataFile.available() && count < maxStoredDataPoints) {
String line = dataFile.readStringUntil('\n');
if (line.length() > 0) {
// Split the line into parts using tabs
int tab1 = line.indexOf(',');
int tab2 = line.indexOf(',', tab1 + 1);
int tab3 = line.indexOf(',', tab2 + 1);
if (tab1 != -1 && tab2 != -1 && tab3 != -1) {
timestamps[count] = line.substring(0, tab1).toInt();
rollData[count] = line.substring(tab1 + 1, tab2).toFloat();
pitchData[count] = line.substring(tab2 + 1, tab3).toFloat();
yawData[count] = line.substring(tab3 + 1).toFloat();
count++;
}
}
}
dataFile.close();
}
}
void updateDataHistory(unsigned long timestamp, float newRoll, float newPitch, float newYaw) {
// Shift data to make space for new data
for (int i = 0; i < maxDataPoints - 1; i++) {
rollData[i] = rollData[i + 1];
pitchData[i] = pitchData[i + 1];
yawData[i] = yawData[i + 1];
timestamps[i] = timestamps[i + 1];
}
// Add new data at the end
rollData[maxDataPoints - 1] = newRoll;
pitchData[maxDataPoints - 1] = newPitch;
yawData[maxDataPoints - 1] = newYaw;
timestamps[maxDataPoints - 1] = timestamp;
// Check if the maximum limit is reached and remove the oldest data
if (timestamps[maxDataPoints - 1] - timestamps[0] > maxStoredDataPoints * interval) {
for (int i = 0; i < maxDataPoints - 1; i++) {
rollData[i] = rollData[i + 1];
pitchData[i] = pitchData[i + 1];
yawData[i] = yawData[i + 1];
timestamps[i] = timestamps[i + 1];
}
rollData[maxDataPoints - 1] = 0; // Reset the last element
pitchData[maxDataPoints - 1] = 0;
yawData[maxDataPoints - 1] = 0;
timestamps[maxDataPoints - 1] = 0;
}
}
void handleWebSocket(uint8_t num, WStype_t type, uint8_t *payload, size_t length) {
// ... (WebSocket event handling code remains unchanged)
}
void sendWebSocketData() {
unsigned long timestamp = millis();
String payload = "{\"timestamps\": [";
String rollPayload = "\"roll\": [";
String pitchPayload = "\"pitch\": [";
String yawPayload = "\"yaw\": [";
for (int i = 0; i < maxDataPoints; i++) {
if (i > 0) {
payload += ",";
rollPayload += ",";
pitchPayload += ",";
yawPayload += ",";
}
payload += timestamps[i];
rollPayload += rollData[i];
pitchPayload += pitchData[i];
yawPayload += yawData[i];
}
payload += "," + String(timestamp) + "]";
rollPayload += "," + String(roll) + "]";
pitchPayload += "," + String(pitch) + "]";
yawPayload += "," + String(yaw) + "]";
payload += "," + rollPayload + "," + pitchPayload + "," + yawPayload + "}";
webSocket.broadcastTXT(payload);
updateDataHistory(timestamp, roll, pitch, yaw);
}
void handleNotFound() {
String message = "File Not Found\n\n";
message += "URI: ";
message += server.uri();
message += "\nMethod: ";
message += (server.method() == HTTP_GET) ? "GET" : "POST";
message += "\nArguments: ";
message += server.args();
message += "\n";
for (uint8_t i = 0; i < server.args(); i++) {
message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
}
server.send(404, "text/plain", message);
}