42 Commits

Author SHA1 Message Date
ishak jmilou.ishak
06dd2d9f48 fix: streamline Kobuki communication startup and reconnection logic 2025-01-09 15:52:22 +01:00
ishak jmilou.ishak
7d3a5fa9a3 fix: improve Kobuki communication startup and reconnection logging 2025-01-09 15:43:45 +01:00
ishak jmilou.ishak
3bca04053a fix: add reconnect attempt logging for Kobuki communication 2025-01-09 15:36:28 +01:00
ishak jmilou.ishak
82363d393c fix: add reconnection logic for Kobuki in main loop 2025-01-09 15:32:30 +01:00
ishak jmilou.ishak
1964589abc fix: update sensor_data function to process and store JSON message from kobuki 2025-01-09 14:57:56 +01:00
ishak jmilou.ishak
8d339851dd commented sensor data 2025-01-09 14:51:17 +01:00
ishak jmilou.ishak
79c2073d29 want to check data 2025-01-09 14:50:41 +01:00
ishak jmilou.ishak
ffbf3347f4 removed parameter 2025-01-09 14:37:26 +01:00
ishak jmilou.ishak
64452b78b4 fix: retrieve JSON data directly in sensor_data function 2025-01-09 14:36:57 +01:00
ishak jmilou.ishak
c87ebc565c fix: parse JSON message before saving sensor data 2025-01-09 14:34:26 +01:00
ishak jmilou.ishak
39659fffab fix: correct variable name in on_message function to use 'data' instead of 'kobuki_message' 2025-01-09 14:30:33 +01:00
ishak jmilou.ishak
a4ae0170a0 refactor: update sensor_data function to accept data directly instead of JSON string 2025-01-09 14:29:54 +01:00
ishak jmilou.ishak
ed475cd19f split the data 2025-01-09 14:28:04 +01:00
ishak jmilou.ishak
a9c37ec470 fix: ensure app context is set when saving sensor data 2025-01-09 14:21:06 +01:00
ishak jmilou.ishak
369120b16b feat: save sensor data to the database upon receiving message 2025-01-09 14:19:01 +01:00
ishak jmilou.ishak
ec71028270 wrote own functions for sending data 2025-01-09 14:16:16 +01:00
ishak jmilou.ishak
7560b0f67a changed to 2 functions 2025-01-09 14:09:33 +01:00
ishak jmilou.ishak
331de940cc fixed variable name 2025-01-09 14:06:13 +01:00
ishak jmilou.ishak
ec0b32a221 changed name 2025-01-09 14:04:40 +01:00
ishak jmilou.ishak
3aa77f5314 feat: insert sensor data into the database upon command execution 2025-01-09 14:03:24 +01:00
ishak jmilou.ishak
e59f235b91 changed db send data to other function 2025-01-09 13:58:34 +01:00
ishak jmilou.ishak
2e4f048ed9 feat: call save_sensor_data function upon receiving kobuki/data message 2025-01-09 13:56:45 +01:00
ishak jmilou.ishak
9a3829cdb2 feat: add save_sensor_data function to insert sensor data into the database 2025-01-09 13:54:15 +01:00
ishak jmilou.ishak
015b2db819 refactor: change startCommunication return type from bool to void 2025-01-09 13:48:01 +01:00
ishak jmilou.ishak
6ef739f794 removed thread 2025-01-09 13:40:47 +01:00
ishak jmilou.ishak
ec2a08c656 refactor: comment out connection check in sendKobukiData function 2025-01-09 13:38:18 +01:00
ishak jmilou.ishak
89421ccf34 replaced connect 2025-01-09 13:31:41 +01:00
ishak jmilou.ishak
1c081451aa made thread to wait 1 sec before reconnect 2025-01-09 13:29:25 +01:00
ishak jmilou.ishak
2396d61eae fix: update on_message function signature to include userdata parameter 2025-01-09 13:24:57 +01:00
ishak jmilou.ishak
fa17893b1b fix: update connection check method in sendKobukiData function 2025-01-09 13:21:14 +01:00
ishak jmilou.ishak
07d88eef7d fix: improve connection handling and logging in KobukiDriver 2025-01-09 13:19:00 +01:00
ishak jmilou.ishak
d066f68ad2 replaced /data endpoint 2025-01-09 12:56:25 +01:00
ishak jmilou.ishak
d90ac72591 test older version 2025-01-09 12:52:11 +01:00
ishak jmilou.ishak
a2aa80804e fix: move mqtt_client.on_message assignment under function definition 2025-01-09 12:47:39 +01:00
ishak jmilou.ishak
612af45f59 refactor control.html to update robot image source and improve structure 2025-01-09 12:39:45 +01:00
ishak jmilou.ishak
3ff1b22346 uncomment robot image in control.html 2025-01-09 12:35:17 +01:00
ishak jmilou.ishak
1a4056ff77 return raw kobuki_message instead of jsonify in data endpoint 2025-01-09 12:02:36 +01:00
ishak jmilou.ishak
8517a0d558 comment out image update interval in script.js 2025-01-09 12:00:02 +01:00
ishak jmilou.ishak
092e4f5aeb remove camera code 2025-01-09 11:59:34 +01:00
ishak jmilou.ishak
9eb9822cff remove unused event listeners and redundant data fetching logic from script.js 2025-01-09 11:58:48 +01:00
ishak jmilou.ishak
fe64267307 added js from main branch 2025-01-09 11:56:54 +01:00
ishak jmilou.ishak
72a0fadef8 add UML diagram for system architecture and clean up MQTT client initialization 2025-01-09 11:45:19 +01:00
6 changed files with 138 additions and 41 deletions

View File

@@ -0,0 +1,51 @@
```mermaid
classDiagram
class CKobuki {
+enableCommands(bool commands)
+loop(void *user_data, TKobukiData &Kobuki_data)
+startCommunication(char *portname, bool CommandsEnabled, void *userDataL)
+measure()
+setLed(int led1, int led2)
+setTranslationSpeed(int mmpersec)
+setRotationSpeed(double radpersec)
+setArcSpeed(int mmpersec, int radius)
+setSound(int noteinHz, int duration)
+setPower(int value)
+goStraight(long double distance)
+forward(int speedvalue)
+doRotation(long double th)
}
class FlaskApp {
+on_message(client, message)
+get_db()
+close_db(error)
+index()
+control()
+move()
}
class MQTTClient {
+connect()
+subscribe(topic)
+getLastMessage()
+isConnected()
}
FlaskApp --> MQTTClient : uses
FlaskApp --> CKobuki : controls
class RPI {
+KobukiCommunication()
+ESP32Communication()
+Camera()
}
class ESP32 {
+TVOC()
+DHT11()
}
RPI --> MQTTClient : communicates
MQTTClient --> CKobuki : communicates
RPI --> ESP32 : communicates

View File

@@ -68,28 +68,14 @@ int CKobuki::connect(char *comportT) {
HCom = open(comportT, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (HCom == -1) {
printf("Kobuki connected\n");
std::cerr <<"unable to connect. retry in 1 second" << std::endl;
return HCom;
} else {
set_interface_attribs2(HCom, B115200,
0); // set speed to 115,200 bps, 8n1 (no parity)
set_interface_attribs2(HCom, B115200,0); // set speed to 115,200 bps, 8n1 (no parity)
set_blocking2(HCom, 0); // set no blocking
/* struct termios settings;
tcgetattr(HCom, &settings);
cfsetospeed(&settings, B115200); // baud rate
settings.c_cflag &= ~PARENB; // no parity
settings.c_cflag &= ~CSTOPB; // 1 stop bit
settings.c_cflag &= ~CSIZE;
settings.c_cflag |= CS8 | CLOCAL; // 8 bits
settings.c_lflag &= ~ICANON; // canonical mode
settings.c_cc[VTIME]=1;
settings.c_oflag &= ~OPOST; // raw output
tcsetattr(HCom, TCSANOW, &settings); // apply the settings*/
tcflush(HCom, TCOFLUSH);
printf("Kobuki connected\n");
std::cout<<"Kobuki connected" << std::endl;
return HCom;
}
}
@@ -252,7 +238,7 @@ void CKobuki::setSound(int noteinHz, int duration) {
pocet = write(HCom, &message, 9);
}
bool CKobuki::startCommunication(char *portname, bool CommandsEnabled,
void CKobuki::startCommunication(char *portname, bool CommandsEnabled,
void *userDataL) {
if(connect(portname) != -1){
enableCommands(CommandsEnabled);

View File

@@ -60,7 +60,7 @@ public:
long loop(void *user_data, TKobukiData &Kobuki_data);
bool startCommunication(char *portname,bool CommandsEnabled,void *userDataL);
void startCommunication(char *portname,bool CommandsEnabled,void *userDataL);
int measure(); //thread function, contains an infinite loop and reads data
void setLed(int led1 = 0, int led2 = 0); //led1 green/red 2/1, //led2 green/red 2/1
void setTranslationSpeed(int mmpersec);

View File

@@ -1,13 +1,10 @@
#include <iostream>
#include <cmath>
#include <thread>
#include "MQTT/MqttClient.h"
#include "KobukiDriver/CKobuki.h"
#include <opencv4/opencv2/opencv.hpp>
#include <opencv4/opencv2/core.hpp>
#include <thread>
using namespace std;
using namespace cv;
CKobuki robot;
@@ -15,21 +12,24 @@ CKobuki robot;
std::string readMQTT();
void parseMQTT(std::string message);
void CapnSend();
// ip, clientID, username, password
MqttClient client("ws://145.92.224.21/ws/", "KobukiRPI", "rpi",
"rpiwachtwoordofzo"); // create a client object
MqttClient client("ws://145.92.224.21/ws/", "KobukiRPI", "rpi","rpiwachtwoordofzo"); // create a client object
std::string message = "stop";
std::string serializeKobukiData(const TKobukiData &data);
void sendKobukiData(TKobukiData &data);
void setup() {
unsigned char *null_ptr(0);
std::cout << "Attempting to start communication with Kobuki..." << std::endl;
robot.startCommunication("/dev/ttyUSB0", true, null_ptr);
if (!robot.isConnected()) {
std::cerr << "Failed to start communication with Kobuki." << std::endl;
} else {
std::cout << "Successfully started communication with Kobuki." << std::endl;
}
// connect mqtt server and sub to commands
client.connect();
client.subscribe("home/commands");
}
int main() {
@@ -40,6 +40,16 @@ int main() {
std::thread sendMqtt([&]() { sendKobukiData(robot.parser.data); });
while (true) {
if (!robot.isConnected()) {
std::cout << "Kobuki is not connected anymore. Reconnecting..." << std::endl;
robot.startCommunication("/dev/ttyUSB0", true, nullptr);
while (!robot.isConnected()) {
std::cout << "Attempting to reconnect..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
std::cout << "Reconnected to Kobuki." << std::endl;
}
std::string message = readMQTT();
if (!message.empty()) {
parseMQTT(message);
@@ -62,7 +72,7 @@ std::string readMQTT() {
// Add a small delay to avoid busy-waiting
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return message;
return lastMessage;
}
void parseMQTT(std::string message) {
@@ -326,6 +336,13 @@ std::string serializeKobukiData(const TKobukiData &data) {
// needed it so it can be threaded
void sendKobukiData(TKobukiData &data) {
while (true) {
// if(!robot.isConnected()){
// std::cout << "Kobuki is not connected anymore" << std::endl;
// robot.startCommunication("/dev/ttyUSB0", true, nullptr);
// while(!robot.isConnected()){
// std::this_thread::sleep_for(std::chrono::seconds(1));
// }
// }
client.publishMessage("kobuki/data", serializeKobukiData(data));
std::cout << "Sent data" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));

View File

@@ -1,6 +1,7 @@
from flask import Flask, request, render_template, jsonify, g
import paho.mqtt.client as mqtt
import mysql.connector
import json
app = Flask(__name__)
@@ -9,10 +10,12 @@ kobuki_message = ""
latest_image = None
# Globale MQTT setup
def on_message(client, message):
def on_message(client,userdata, message):
global kobuki_message, latest_image
if message.topic == "kobuki/data":
kobuki_message = str(message.payload.decode("utf-8"))
with app.app_context():
sensor_data(kobuki_message) # Sla de data op in de database
elif message.topic == "kobuki/cam":
latest_image = message.payload
@@ -24,6 +27,9 @@ mqtt_client.connect("localhost", 1884, 60)
mqtt_client.loop_start()
mqtt_client.subscribe("kobuki/data")
mqtt_client.on_message = on_message # this line needs to be under the function definition otherwise it can't find which function it needs to use
# Database connectie-functie
def get_db():
if 'db' not in g: # 'g' is specifiek voor een request en leeft zolang een request duurt
@@ -54,6 +60,11 @@ def control():
else:
return ('Unauthorized', 401, {'WWW-Authenticate': 'Basic realm="Login Required"'})
@app.route('/data', methods=['GET'])
def data():
return kobuki_message
@app.route('/move', methods=['POST'])
def move():
data = request.get_json()
@@ -65,19 +76,14 @@ def move():
db_connection = get_db()
cursor = db_connection.cursor()
sql = "INSERT INTO command (command) VALUES (%s)"
value = direction
cursor.execute(sql, (value,))
sql_command = "INSERT INTO command (command) VALUES (%s)"
cursor.execute(sql_command, (direction,))
db_connection.commit()
cursor.close()
db_connection.close()
return jsonify({"status": "success", "direction": direction})
@app.route('/data', methods=['GET'])
def data():
return jsonify(kobuki_message)
@app.route("/database")
def database():
db = get_db()
@@ -87,5 +93,39 @@ def database():
cursor.close()
return str(rows)
def sensor_data(kobuki_message):
try:
# Parse de JSON-string naar een Python-dictionary
data = json.loads(kobuki_message)
# Maak een lijst van tuples met de naam en waarde van elk veld
sensor_data_tuples = [(name, float(value)) for name, value in data.items() if isinstance(value, (int, float))]
# Extra informatie of nested data (zoals "extraInfo" of "gyroData") kun je apart verwerken
if "extraInfo" in data:
for key, value in data["extraInfo"].items():
sensor_data_tuples.append((f"extraInfo_{key}", float(value)))
if "gyroData" in data:
for i, gyro in enumerate(data["gyroData"]):
for axis, value in gyro.items():
sensor_data_tuples.append((f"gyroData_{i}_{axis}", float(value)))
# Database-insert
db = get_db()
cursor = db.cursor()
# Zorg dat je tabel `kobuki_data` kolommen heeft: `name` en `value`
sql_sensor = "INSERT INTO kobuki_data (name, value) VALUES (%s, %s)"
cursor.executemany(sql_sensor, sensor_data_tuples)
# Commit en sluit de cursor
db.commit()
cursor.close()
except json.JSONDecodeError as e:
print(f"JSON decode error: {e}")
except mysql.connector.Error as err:
print(f"Database error: {err}")
if __name__ == '__main__':
app.run(debug=True, port=5000)

View File

@@ -1,6 +1,8 @@
{% extends 'base.html' %} {% block head %}
{% extends 'base.html' %}
{% block head %}
<link rel="stylesheet" href="../static/style.css" />
{% endblock %} {% block content %}
{% endblock %}
{% block content %}
<!DOCTYPE html>
<html lang="en">
<head>
@@ -11,8 +13,8 @@
</head>
<body>
<div class="container">
<div class="image-section">
<!-- <img src="kobuki.jpg" alt="Kobuki Robot" id="robot-image" /> -->
<div class="robot-image">
<img src="/image" alt="Kobuki Camera Feed" id="robot-image" />
</div>
<div class="button-section">
<form id="form" action="/move" method="post">
@@ -42,6 +44,7 @@
</table>
</div>
</div>
<script src="../static/script.js"></script>
</body>
</html>