diff --git a/.config/conky/conky-esp-config/10-battery_watchdog/battery_watchdog.sh b/.config/conky/conky-esp-config/10-battery_watchdog/battery_watchdog.sh
index 64f20cc..3e0779a 100755
--- a/.config/conky/conky-esp-config/10-battery_watchdog/battery_watchdog.sh
+++ b/.config/conky/conky-esp-config/10-battery_watchdog/battery_watchdog.sh
@@ -9,8 +9,31 @@ DISCONNECTED_THRESHOLD=0.0
LOW_BATTERY_SOUND="warning_low_battery_en_1.mp3"
DISCONNECTED_SOUND="battery_removed.mp3"
DISCONNECTED_PLAYED=false
+ESP32_RECONNECTED=true
+LOW_BATTERY_DURATION=30
+low_battery_start_time=0
while true; do
+ if [ ! -L "/dev/esp-daemon" ] || [ ! -e "/dev/esp-daemon" ]; then
+ ESP32_RECONNECTED=false
+ if [ -f "$BATTERY_STATUS_FILE" ]; then
+ last_modified=$(stat -c %Y "$BATTERY_STATUS_FILE")
+ current_time=$(date +%s)
+ current_content=$(cat "$BATTERY_STATUS_FILE")
+ if (( current_time - last_modified > 3 )) && [ "$current_content" != '{"voltage": 0}' ]; then
+ echo -n '{"voltage": 0}' > "$BATTERY_STATUS_FILE"
+ fi
+ else
+ echo -n '{"voltage": 0}' > "$BATTERY_STATUS_FILE"
+ fi
+ else
+ if [[ "$ESP32_RECONNECTED" == false ]]; then
+ pkill -f "firefox --new-window -P default --kiosk http://${HOSTNAME}-esp.local"
+ (sleep 5 && firefox --new-window -P "default" --kiosk "http://${HOSTNAME}-esp.local" &)
+ ESP32_RECONNECTED=true
+ fi
+ fi
+
if [ -f "$BATTERY_STATUS_FILE" ]; then
voltage=$(jq -r '.voltage' "$BATTERY_STATUS_FILE")
@@ -26,10 +49,18 @@ while true; do
DISCONNECTED_PLAYED=false
if (( $(echo "$voltage < $LOW_BATTERY_THRESHOLD" | bc -l) )); then
- if ! pgrep -x "ffplay" > /dev/null; then
- # Broadcast low battery warning
- ffplay -nodisp -autoexit "$LOW_BATTERY_SOUND" > /dev/null 2>&1 &
+ if [ $low_battery_start_time -eq 0 ]; then
+ low_battery_start_time=$(date +%s)
+ fi
+ current_time=$(date +%s)
+ if (( current_time - low_battery_start_time >= LOW_BATTERY_DURATION )); then
+ if ! pgrep -x "ffplay" > /dev/null; then
+ # Broadcast low battery warning
+ ffplay -nodisp -autoexit "$LOW_BATTERY_SOUND" > /dev/null 2>&1 &
+ fi
fi
+ else
+ low_battery_start_time=0
fi
fi
fi
diff --git a/.config/conky/conky_entry.sh b/.config/conky/conky_entry.sh
index fff0b55..5f4b1e4 100755
--- a/.config/conky/conky_entry.sh
+++ b/.config/conky/conky_entry.sh
@@ -38,10 +38,16 @@ $DIR/conky-esp-config/10-network_watchdog/network_watchdog.sh &
# # Run the Web Pannel essential scripts
# /home/ditrobotics/DIT-Scripts/web-ui/scripts/aio.sh &
-# # Wait for the web server to start
-# while ! nc -z localhost 5000; do
-# sleep 0.1
-# done
-#
-# # Open Robot UI in Firefox
-# firefox http://localhost:5000 http://${HOSTNAME}-esp.local &
+
+# Wait for the web server to start
+while ! nc -z localhost 8080; do
+ sleep 1
+done
+
+# Open Robot UI
+chromium --kiosk \
+ "http://localhost:8080/?ds=foxglove-websocket&ds.url=ws://$(hostname -I | cut -d" " -f1):8765" \
+ &
+firefox --new-window -P "default" --kiosk \
+ "http://${HOSTNAME}-esp.local" \
+ &
diff --git a/.config/firefox/firefox_profile_backup.tar.gz b/.config/firefox/firefox_profile_backup.tar.gz
new file mode 100644
index 0000000..46dedd8
Binary files /dev/null and b/.config/firefox/firefox_profile_backup.tar.gz differ
diff --git a/.mozilla/firefox/dit_config.default/handlers.json b/.mozilla/firefox/dit_config.default/handlers.json
deleted file mode 100644
index 4e79b3a..0000000
--- a/.mozilla/firefox/dit_config.default/handlers.json
+++ /dev/null
@@ -1 +0,0 @@
-{"defaultHandlersVersion":{},"mimeTypes":{"application/pdf":{"action":3,"extensions":["pdf"]},"image/webp":{"action":3,"extensions":["webp"]},"image/avif":{"action":3,"extensions":["avif"]}},"schemes":{"mailto":{"stubEntry":true,"handlers":[null,{"name":"Gmail","uriTemplate":"https://mail.google.com/mail/?extsrc=mailto&url=%s"}]}},"isDownloadsImprovementsAlreadyMigrated":false}
\ No newline at end of file
diff --git a/.mozilla/firefox/dit_config.default/prefs.js b/.mozilla/firefox/dit_config.default/prefs.js
deleted file mode 100644
index 5609449..0000000
--- a/.mozilla/firefox/dit_config.default/prefs.js
+++ /dev/null
@@ -1,163 +0,0 @@
-// Mozilla User Preferences
-
-// DO NOT EDIT THIS FILE.
-//
-// If you make changes to this file while the application is running,
-// the changes will be overwritten when the application exits.
-//
-// To change a preference value, you can either:
-// - modify it via the UI (e.g. via about:config in the browser); or
-// - set it within a user.js file in your profile.
-
-user_pref("app.normandy.first_run", false);
-user_pref("app.normandy.migrationsApplied", 12);
-user_pref("app.normandy.user_id", "ac565930-a2ec-419d-89d1-5c53801fa6ff");
-user_pref("app.update.lastUpdateTime.addon-background-update-timer", 1736355006);
-user_pref("app.update.lastUpdateTime.browser-cleanup-thumbnails", 1736355006);
-user_pref("app.update.lastUpdateTime.recipe-client-addon-run", 1736355006);
-user_pref("app.update.lastUpdateTime.region-update-timer", 1736355006);
-user_pref("app.update.lastUpdateTime.rs-experiment-loader-timer", 1736355254);
-user_pref("app.update.lastUpdateTime.services-settings-poll-changes", 1736355006);
-user_pref("app.update.lastUpdateTime.telemetry_modules_ping", 1736355036);
-user_pref("app.update.lastUpdateTime.xpi-signature-verification", 1736355006);
-user_pref("browser.bookmarks.addedImportButton", true);
-user_pref("browser.bookmarks.editDialog.confirmationHintShowCount", 1);
-user_pref("browser.bookmarks.restore_default_bookmarks", false);
-user_pref("browser.contentblocking.category", "standard");
-user_pref("browser.contextual-services.contextId", "{4d735560-97d9-4035-a98d-293b7d28fac5}");
-user_pref("browser.download.viewableInternally.typeWasRegistered.avif", true);
-user_pref("browser.download.viewableInternally.typeWasRegistered.webp", true);
-user_pref("browser.engagement.fxa-toolbar-menu-button.has-used", true);
-user_pref("browser.laterrun.bookkeeping.profileCreationTime", 1736354976);
-user_pref("browser.laterrun.bookkeeping.sessionCount", 1);
-user_pref("browser.laterrun.enabled", true);
-user_pref("browser.migration.version", 150);
-user_pref("browser.newtabpage.activity-stream.impressionId", "{ea808bbc-5938-49d7-9b39-a2126736b535}");
-user_pref("browser.newtabpage.activity-stream.showSponsoredTopSites", false);
-user_pref("browser.newtabpage.blocked", "{\"26UbzFJ7qT9/4DhodHKA1Q==\":1,\"4gPpjkxgZzXPVtuEoAL9Ig==\":1,\"eV8/WsSLxHadrTL1gAxhug==\":1,\"gLv0ja2RYVgxKdp0I5qwvA==\":1,\"T9nJot5PurhJSy8n038xGA==\":1}");
-user_pref("browser.newtabpage.storageVersion", 1);
-user_pref("browser.pageActions.persistedActions", "{\"ids\":[\"bookmark\"],\"idsInUrlbar\":[\"bookmark\"],\"idsInUrlbarPreProton\":[],\"version\":1}");
-user_pref("browser.pagethumbnails.storage_version", 3);
-user_pref("browser.privatebrowsing.autostart", true);
-user_pref("browser.proton.toolbar.version", 3);
-user_pref("browser.region.update.updated", 1736354976);
-user_pref("browser.safebrowsing.provider.google4.lastupdatetime", "1736355006624");
-user_pref("browser.safebrowsing.provider.google4.nextupdatetime", "1736356804624");
-user_pref("browser.safebrowsing.provider.mozilla.lastupdatetime", "1736354981502");
-user_pref("browser.safebrowsing.provider.mozilla.nextupdatetime", "1736376581502");
-user_pref("browser.search.region", "TW");
-user_pref("browser.search.serpEventTelemetryCategorization.regionEnabled", false);
-user_pref("browser.search.totalSearches", 2);
-user_pref("browser.shell.checkDefaultBrowser", true);
-user_pref("browser.shell.mostRecentDateSetAsDefault", "1736355734");
-user_pref("browser.startup.couldRestoreSession.count", 1);
-user_pref("browser.startup.homepage_override.buildID", "20250106230035");
-user_pref("browser.startup.homepage_override.mstone", "134.0");
-user_pref("browser.startup.lastColdStartupCheck", 1736355734);
-user_pref("browser.uiCustomization.state", "{\"placements\":{\"widget-overflow-fixed-list\":[],\"unified-extensions-area\":[],\"nav-bar\":[\"back-button\",\"forward-button\",\"stop-reload-button\",\"customizableui-special-spring1\",\"urlbar-container\",\"customizableui-special-spring2\",\"save-to-pocket-button\",\"downloads-button\",\"fxa-toolbar-menu-button\",\"unified-extensions-button\"],\"toolbar-menubar\":[\"menubar-items\"],\"TabsToolbar\":[\"firefox-view-button\",\"tabbrowser-tabs\",\"new-tab-button\",\"alltabs-button\"],\"vertical-tabs\":[],\"PersonalToolbar\":[\"import-button\",\"personal-bookmarks\"]},\"seen\":[\"save-to-pocket-button\",\"developer-button\"],\"dirtyAreaCache\":[\"nav-bar\",\"vertical-tabs\",\"PersonalToolbar\",\"toolbar-menubar\",\"TabsToolbar\"],\"currentVersion\":20,\"newElementCount\":2}");
-user_pref("browser.urlbar.placeholderName", "Google");
-user_pref("browser.urlbar.placeholderName.private", "Google");
-user_pref("browser.urlbar.quicksuggest.migrationVersion", 2);
-user_pref("browser.urlbar.quicksuggest.scenario", "history");
-user_pref("browser.urlbar.suggest.recentsearches", false);
-user_pref("datareporting.dau.cachedUsageProfileID", "beefbeef-beef-beef-beef-beeefbeefbee");
-user_pref("datareporting.healthreport.uploadEnabled", false);
-user_pref("datareporting.policy.dataSubmissionPolicyAcceptedVersion", 2);
-user_pref("datareporting.policy.dataSubmissionPolicyNotifiedTime", "1736354977918");
-user_pref("distribution.canonical-002.bookmarksProcessed", true);
-user_pref("distribution.iniFile.exists.appversion", "134.0");
-user_pref("distribution.iniFile.exists.value", true);
-user_pref("doh-rollout.doneFirstRun", true);
-user_pref("doh-rollout.home-region", "TW");
-user_pref("dom.push.userAgentID", "1de31153587846f9ad3f9bce153659f4");
-user_pref("extensions.activeThemeID", "default-theme@mozilla.org");
-user_pref("extensions.blocklist.pingCountVersion", 0);
-user_pref("extensions.databaseSchema", 37);
-user_pref("extensions.formautofill.creditCards.reauth.optout", "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECBhSdL+RzwAfBAgXglKc4Ixa/w==");
-user_pref("extensions.getAddons.cache.lastUpdate", 1736355006);
-user_pref("extensions.getAddons.databaseSchema", 6);
-user_pref("extensions.installedDistroAddon.langpack-en-US@firefox.mozilla.org", true);
-user_pref("extensions.lastAppBuildId", "20250106230035");
-user_pref("extensions.lastAppVersion", "134.0");
-user_pref("extensions.lastPlatformVersion", "134.0");
-user_pref("extensions.pendingOperations", false);
-user_pref("extensions.pictureinpicture.enable_picture_in_picture_overrides", true);
-user_pref("extensions.quarantinedDomains.list", "autoatendimento.bb.com.br,ibpf.sicredi.com.br,ibpj.sicredi.com.br,internetbanking.caixa.gov.br,www.ib12.bradesco.com.br,www2.bancobrasil.com.br");
-user_pref("extensions.systemAddonSet", "{\"schema\":1,\"addons\":{}}");
-user_pref("extensions.ui.dictionary.hidden", true);
-user_pref("extensions.ui.lastCategory", "addons://discover/");
-user_pref("extensions.ui.locale.hidden", false);
-user_pref("extensions.ui.sitepermission.hidden", true);
-user_pref("extensions.webcompat.enable_shims", true);
-user_pref("extensions.webcompat.perform_injections", true);
-user_pref("extensions.webcompat.perform_ua_overrides", true);
-user_pref("extensions.webextensions.ExtensionStorageIDB.migrated.screenshots@mozilla.org", true);
-user_pref("extensions.webextensions.uuids", "{\"formautofill@mozilla.org\":\"de60c220-2dab-4749-b9d0-bacce75f35bd\",\"pictureinpicture@mozilla.org\":\"da149b0a-01fe-4973-ab75-af8b65fe05a9\",\"screenshots@mozilla.org\":\"ae97e3d5-5b3d-4226-874f-78f4de01d573\",\"webcompat-reporter@mozilla.org\":\"52b26a64-b663-4440-ae9b-3efc600cd693\",\"webcompat@mozilla.org\":\"e2aad606-8730-4c8f-a503-b44457c61bfd\",\"default-theme@mozilla.org\":\"82c1ba0f-8305-4260-9f51-fc5193564243\",\"addons-search-detection@mozilla.com\":\"c67e7db1-a5b1-4ac1-9aa0-e742646da03c\"}");
-user_pref("gecko.handlerService.defaultHandlersVersion", 1);
-user_pref("media.gmp-gmpopenh264.abi", "x86_64-gcc3");
-user_pref("media.gmp-gmpopenh264.hashValue", "53a58bfb4c8124ad4f7655b99bfdea290033a085e0796b19245b33b91c0948fdac9f0c3e817130b352493a65d9a7a0fc8a7c1eedc618cdaa2b4580734a11cd9c");
-user_pref("media.gmp-gmpopenh264.lastDownload", 1736355007);
-user_pref("media.gmp-gmpopenh264.lastInstallStart", 1736355007);
-user_pref("media.gmp-gmpopenh264.lastUpdate", 1736355007);
-user_pref("media.gmp-gmpopenh264.version", "2.3.2");
-user_pref("media.gmp.storage.version.observed", 1);
-user_pref("pdfjs.enabledCache.state", true);
-user_pref("pdfjs.migrationVersion", 2);
-user_pref("pref.general.disable_button.default_browser", false);
-user_pref("privacy.bounceTrackingProtection.hasMigratedUserActivationData", true);
-user_pref("privacy.clearHistory.siteSettings", true);
-user_pref("privacy.sanitize.clearOnShutdown.hasMigratedToNewPrefs2", true);
-user_pref("privacy.sanitize.cpd.hasMigratedToNewPrefs2", true);
-user_pref("privacy.sanitize.pending", "[]");
-user_pref("services.settings.blocklists.addons-bloomfilters.last_check", 1736355006);
-user_pref("services.settings.blocklists.gfx.last_check", 1736355006);
-user_pref("services.settings.clock_skew_seconds", 0);
-user_pref("services.settings.last_etag", "\"1736347628397\"");
-user_pref("services.settings.last_update_seconds", 1736355006);
-user_pref("services.settings.main.addons-manager-settings.last_check", 1736355006);
-user_pref("services.settings.main.anti-tracking-url-decoration.last_check", 1736355006);
-user_pref("services.settings.main.cfr.last_check", 1736355006);
-user_pref("services.settings.main.cookie-banner-rules-list.last_check", 1736355006);
-user_pref("services.settings.main.devtools-compatibility-browsers.last_check", 1736355006);
-user_pref("services.settings.main.devtools-devices.last_check", 1736355006);
-user_pref("services.settings.main.doh-config.last_check", 1736355006);
-user_pref("services.settings.main.doh-providers.last_check", 1736355006);
-user_pref("services.settings.main.fingerprinting-protection-overrides.last_check", 1736355006);
-user_pref("services.settings.main.hijack-blocklists.last_check", 1736355006);
-user_pref("services.settings.main.language-dictionaries.last_check", 1736355006);
-user_pref("services.settings.main.message-groups.last_check", 1736355006);
-user_pref("services.settings.main.newtab-wallpapers-v2.last_check", 1736355006);
-user_pref("services.settings.main.nimbus-desktop-experiments.last_check", 1736355006);
-user_pref("services.settings.main.nimbus-secure-experiments.last_check", 1736355006);
-user_pref("services.settings.main.normandy-recipes-capabilities.last_check", 1736355006);
-user_pref("services.settings.main.partitioning-exempt-urls.last_check", 1736355006);
-user_pref("services.settings.main.password-recipes.last_check", 1736355006);
-user_pref("services.settings.main.password-rules.last_check", 1736355006);
-user_pref("services.settings.main.query-stripping.last_check", 1736355006);
-user_pref("services.settings.main.search-categorization.last_check", 1736355006);
-user_pref("services.settings.main.search-config-icons.last_check", 1736355006);
-user_pref("services.settings.main.search-config-overrides-v2.last_check", 1736355006);
-user_pref("services.settings.main.search-config-v2.last_check", 1736355006);
-user_pref("services.settings.main.search-default-override-allowlist.last_check", 1736355006);
-user_pref("services.settings.main.search-telemetry-v2.last_check", 1736355006);
-user_pref("services.settings.main.sites-classification.last_check", 1736355006);
-user_pref("services.settings.main.top-sites.last_check", 1736355006);
-user_pref("services.settings.main.tracking-protection-lists.last_check", 1736355006);
-user_pref("services.settings.main.translations-models.last_check", 1736355006);
-user_pref("services.settings.main.translations-wasm.last_check", 1736355006);
-user_pref("services.settings.main.url-classifier-skip-urls.last_check", 1736355006);
-user_pref("services.settings.main.url-parser-default-unknown-schemes-interventions.last_check", 1736355006);
-user_pref("services.settings.main.urlbar-persisted-search-terms.last_check", 1736355006);
-user_pref("services.settings.main.websites-with-shared-credential-backends.last_check", 1736355006);
-user_pref("services.sync.clients.lastSync", "0");
-user_pref("services.sync.declinedEngines", "");
-user_pref("services.sync.globalScore", 0);
-user_pref("services.sync.nextSync", 0);
-user_pref("sidebar.backupState", "{\"width\":\"\",\"command\":\"\"}");
-user_pref("signon.management.page.os-auth.optout", "MDIEEPgAAAAAAAAAAAAAAAAAAAEwFAYIKoZIhvcNAwcECJSp9JdqOXIdBAhZActYtY4k6Q==");
-user_pref("toolkit.startup.last_success", 1736355733);
-user_pref("toolkit.telemetry.cachedClientID", "c0ffeec0-ffee-c0ff-eec0-ffeec0ffeec0");
-user_pref("toolkit.telemetry.cachedProfileGroupID", "decafdec-afde-cafd-ecaf-decafdecafde");
-user_pref("toolkit.telemetry.previousBuildID", "20250106230035");
-user_pref("toolkit.telemetry.reportingpolicy.firstRun", false);
-user_pref("trailhead.firstrun.didSeeAboutWelcome", true);
diff --git a/15-user_preference.sh b/15-user_preference.sh
index d27af8d..6de76f9 100755
--- a/15-user_preference.sh
+++ b/15-user_preference.sh
@@ -1,5 +1,7 @@
#!/bin/bash
+DIT_HOME="/home/ditrobotics"
+
# Check if the current user is root
if [ "$(id -u)" != "0" ]; then
echo -e "This script must be run as root. \nPlease run again with 'sudo $0'"
@@ -11,30 +13,36 @@ restore_firefox() {
echo -e "\033[32mRestoring firefox user preference...\033[0m"
# You need to open firefox first to create the folder
read -p "Please open firefox first. Press [Enter] key to continue..."
- find /home/ditrobotics/snap/firefox/common/.mozilla/firefox/ -type d -name "*.default" -exec cp -r /home/ditrobotics/DIT-Scripts/.mozilla/firefox/dit_config.default/* {} \;
+ if [ -d "$DIT_HOME/snap/firefox/common/.mozilla/firefox/" ]; then
+ rm -rf "$DIT_HOME/snap/firefox/common/.mozilla/firefox/"
+ fi
+ mkdir -p "$DIT_HOME/snap/firefox/common/.mozilla/firefox/"
+ tar -xzf "$DIT_HOME/DIT-Scripts/.config/firefox/firefox_profile_backup.tar.gz" -C "$DIT_HOME/snap/firefox/common/.mozilla/firefox/"
+ chown -R ditrobotics:ditrobotics "$DIT_HOME/snap/firefox/common/.mozilla/firefox/"
+ chmod -R 700 "$DIT_HOME/snap/firefox/common/.mozilla/firefox/"
}
# Restore desktop configuration
restore_desktop() {
echo -e "\033[32mRestoring desktop user preference...\033[0m"
- cp -r /home/ditrobotics/DIT-Scripts/desktop/* /home/ditrobotics/Desktop/
+ cp -r "$DIT_HOME/DIT-Scripts/desktop/"* "$DIT_HOME/Desktop/"
if [ ! -d /home/share/scripts/ ]; then
mkdir -p /home/share/scripts/
fi
- cp -r /home/ditrobotics/DIT-Scripts/share/scripts/* /home/share/scripts/
+ cp -r "$DIT_HOME/DIT-Scripts/share/scripts/"* /home/share/scripts/
if [ ! -d /home/share/data/ ]; then
mkdir -p /home/share/data/
fi
- cp -r /home/ditrobotics/DIT-Scripts/share/data/* /home/share/data/
+ cp -r "$DIT_HOME/DIT-Scripts/share/data/"* /home/share/data/
}
# Restore plymouth theme configuration
restore_plymouth() {
echo -e "\033[32mRestoring plymouth theme preference...\033[0m"
- cp -r /home/ditrobotics/DIT-Scripts/system/plymouth-themes/abstract_ring_alt /usr/share/plymouth/themes/
+ cp -r "$DIT_HOME/DIT-Scripts/system/plymouth-themes/abstract_ring_alt" /usr/share/plymouth/themes/
update-alternatives --install /usr/share/plymouth/themes/default.plymouth default.plymouth /usr/share/plymouth/themes/abstract_ring_alt/abstract_ring_alt.plymouth 100
echo -e "\033[32mSelect the number for installed theme...\033[0m"
update-alternatives --config default.plymouth
diff --git a/app/esp_daemon/arduino/ESP_Daemon/data/index.html b/app/esp_daemon/arduino/ESP_Daemon/data/index.html
index 695732b..b3cacbd 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/data/index.html
+++ b/app/esp_daemon/arduino/ESP_Daemon/data/index.html
@@ -37,6 +37,23 @@
ROBOT STATUS PANEL
+
diff --git a/app/esp_daemon/arduino/ESP_Daemon/data/script.js b/app/esp_daemon/arduino/ESP_Daemon/data/script.js
index 83e7a9f..8043108 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/data/script.js
+++ b/app/esp_daemon/arduino/ESP_Daemon/data/script.js
@@ -412,6 +412,7 @@ window.addEventListener('orientationchange', function() {
document.addEventListener('DOMContentLoaded', function() {
const enableButton = document.getElementById('emergencyEnable');
const disableButton = document.getElementById('emergencyDisable');
+ const actionButton = document.getElementById('actionButton');
if (enableButton && disableButton) {
// Enable emergency button handler
@@ -428,6 +429,59 @@ document.addEventListener('DOMContentLoaded', function() {
enableButton.classList.remove('active');
});
}
+
+ // Modern action button with short and long press functionality
+ if (actionButton) {
+ let pressTimer;
+ let longPress = false;
+
+ // Mouse/touch down event - start timer
+ actionButton.addEventListener('mousedown', startPressTimer);
+ actionButton.addEventListener('touchstart', function(e) {
+ e.preventDefault();
+ startPressTimer();
+ });
+
+ // Mouse/touch up event - if not long press, navigate
+ actionButton.addEventListener('mouseup', handlePressEnd);
+ actionButton.addEventListener('touchend', function(e) {
+ e.preventDefault();
+ handlePressEnd();
+ });
+
+ // If the user moves away while pressing, cancel the long press
+ actionButton.addEventListener('mouseleave', clearPressTimer);
+ actionButton.addEventListener('touchcancel', clearPressTimer);
+
+ function startPressTimer() {
+ clearTimeout(pressTimer);
+ longPress = false;
+
+ // If button is held for 800ms, it's a long press for refresh
+ pressTimer = setTimeout(() => {
+ longPress = true;
+ actionButton.classList.add('loading');
+
+ // Add a small delay to show the animation
+ setTimeout(() => {
+ window.location.reload();
+ }, 500);
+ }, 800);
+ }
+
+ function clearPressTimer() {
+ clearTimeout(pressTimer);
+ }
+
+ function handlePressEnd() {
+ clearTimeout(pressTimer);
+
+ // If it was a short press (not long press), navigate to the specified URL
+ if (!longPress) {
+ window.location.href = 'http://localhost:8080/';
+ }
+ }
+ }
});
// Function to send emergency command to the server
diff --git a/app/esp_daemon/arduino/ESP_Daemon/data/style.css b/app/esp_daemon/arduino/ESP_Daemon/data/style.css
index eef2b06..de7a3d9 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/data/style.css
+++ b/app/esp_daemon/arduino/ESP_Daemon/data/style.css
@@ -278,6 +278,117 @@ p {
font-weight: bold;
}
+/* Floating buttons container */
+.floating-buttons {
+ position: fixed;
+ bottom: 2rem;
+ right: 2rem;
+ display: flex;
+ flex-direction: row;
+ gap: 1rem;
+ z-index: 1000;
+}
+
+/* Modern floating button */
+.modern-floating-btn {
+ position: relative;
+ width: 3.5rem;
+ height: 3.5rem;
+ background-color: var(--primary-color);
+ border-radius: 50%;
+ box-shadow: 0 10px 15px rgba(0, 0, 0, 0.1), 0 4px 6px rgba(0, 0, 0, 0.05);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ overflow: hidden;
+ color: white;
+}
+
+.modern-floating-btn:hover {
+ transform: translateY(-4px);
+ box-shadow: 0 20px 25px rgba(0, 0, 0, 0.15), 0 10px 10px rgba(0, 0, 0, 0.08);
+}
+
+.modern-floating-btn:active {
+ transform: translateY(-2px);
+ box-shadow: 0 15px 20px rgba(0, 0, 0, 0.12), 0 5px 8px rgba(0, 0, 0, 0.06);
+}
+
+/* Button icon */
+.btn-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ transition: all 0.3s ease;
+}
+
+/* Loading animation for long press */
+.btn-loading {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transform: scale(0.5);
+ transition: all 0.3s ease;
+ pointer-events: none;
+}
+
+/* Button tooltip */
+.btn-tooltip {
+ position: absolute;
+ top: -40px;
+ left: 50%;
+ transform: translateX(-50%) scale(0.8);
+ background-color: rgba(0, 0, 0, 0.7);
+ color: white;
+ padding: 0.5rem 1rem;
+ border-radius: 0.5rem;
+ font-size: 0.875rem;
+ white-space: nowrap;
+ opacity: 0;
+ transition: all 0.2s ease;
+ pointer-events: none;
+}
+
+.modern-floating-btn:hover .btn-tooltip {
+ opacity: 1;
+ transform: translateX(-50%) scale(1);
+}
+
+/* Show loading state */
+.modern-floating-btn.loading .btn-icon {
+ opacity: 0;
+ transform: scale(0.5);
+}
+
+.modern-floating-btn.loading .btn-loading {
+ opacity: 1;
+ transform: scale(1);
+}
+
+/* Responsive floating button */
+@media (max-width: 600px) {
+ .floating-buttons {
+ bottom: 1.5rem;
+ right: 1.5rem;
+ gap: 0.8rem;
+ }
+
+ .modern-floating-btn {
+ width: 3rem;
+ height: 3rem;
+ }
+}
+
/* Footer */
.footer {
margin-top: 1rem;
diff --git a/app/esp_daemon/arduino/ESP_Daemon/include/config.h b/app/esp_daemon/arduino/ESP_Daemon/include/config.h
index c2cecf2..08ce7e3 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/include/config.h
+++ b/app/esp_daemon/arduino/ESP_Daemon/include/config.h
@@ -2,33 +2,49 @@
#define CONFIG_H
// WiFi and mDNS
-#define HOSTNAME "DIT-2025-00-ESP"
-#define MDNS_NAME "dit-2025-00-esp"
+#define HOSTNAME "DIT-2025-00-ESP"
+#define MDNS_NAME "dit-2025-00-esp"
// ESP-NOW for SIMA communication
-#define BROADCAST_ADDR { 0x94, 0xa9, 0x90, 0x0b, 0x07, 0x00 }
+// [94:a9:90:07:00:78]---[01]
+// [94:a9:90:06:E6:00]---[02]
+// [94:a9:90:0b:86:d8]---[03]
+// [94:a9:90:05:57:d8]---[04]
+#define SIMA_01 { 0x94, 0xa9, 0x90, 0x07, 0x00, 0x78 }
+#define SIMA_02 { 0x94, 0xa9, 0x90, 0x06, 0xe6, 0x00 }
+#define SIMA_03 { 0x94, 0xa9, 0x90, 0x0b, 0x86, 0xd8 }
+#define SIMA_04 { 0x94, 0xa9, 0x90, 0x05, 0x57, 0xd8 }
// micro-ROS
#define ROS_NODE_NAME "esp_daemon"
-#define ROS_DOMAIN_ID 100
-#define ROS_TIMER_MS 250
+#define ROS_DOMAIN_ID 00
+#define ROS_TIMER_MS 100
#define MROS_TIMEOUT_MS 100
#define MROS_PING_INTERVAL 1000
+
+// Emergency Button
#define RELAY_PIN D2
+#define RELAY_ACTIVE_STATE LOW
+#define RELAY_INITIAL_STATE LOW
+#define ENABLE RELAY_ACTIVE_STATE
+#define DISABLE (!RELAY_ACTIVE_STATE)
// RGB LED strip
-#define LED_PIN D1
-#define LED_COUNT 30
-#define LED_BRIGHTNESS 128
+#define LED_PIN D1
+#define LED_COUNT 40
+#define LED_BRIGHTNESS 128
+#define LED_OVR_DURATION 1000
// Voltmeter - Battery voltage measurement
// | Formula:
// | Vbattf = (VOLTMETER_CALIBRATION * Vbatt / SLIDING_WINDOW_SIZE / 1000.0) + VOLTMETER_OFFSET;
+// | [ R1 = 1.5M ohm, R2 = 220k ohm ] VC = 7.81 OFFSET = 0.65
+// | [ R1 = 1.5M ohm, R2 = 200k ohm ] VC = 8.50 OFFSET = 0.65
// | Note:
// | (A0 == D0) on Xiao ESP32C3
#define VOLTMETER_PIN A0
-#define VOLTMETER_CALIBRATION 7.81
-#define VOLTMETER_OFFSET 0.65
+#define VOLTMETER_CALIBRATION 8.5
+#define VOLTMETER_OFFSET 0.1
#define SLIDING_WINDOW_SIZE 64
#define TIMER_PERIOD_US 1000000
// constexpr uint32_t TIMER_PERIOD_US = 1000000;
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/comm/espnow_comm.cpp b/app/esp_daemon/arduino/ESP_Daemon/lib/comm/espnow_comm.cpp
index 631a296..0db9da9 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/comm/espnow_comm.cpp
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/comm/espnow_comm.cpp
@@ -1,9 +1,15 @@
#include "espnow_comm.h"
#include "config.h"
+
+#include "wifi_config.h"
+
#include
+#include
+#include
+#include
struct_message myData;
-uint8_t broadcastAddress[] = BROADCAST_ADDR;
+std::vector> broadcastAddresses = {SIMA_01, SIMA_02, SIMA_03, SIMA_04};
esp_now_peer_info_t peerInfo;
void OnDataSent(const uint8_t* mac_addr, esp_now_send_status_t status) {
@@ -19,17 +25,27 @@ void initESPNow(){
}
esp_now_register_send_cb(OnDataSent);
- memcpy(peerInfo.peer_addr, broadcastAddress, 6);
- peerInfo.channel = 0;
- peerInfo.encrypt = false;
- if (esp_now_add_peer(&peerInfo) != ESP_OK) {
- Serial.println("ESP-NOW peer add failed");
+ for (auto& address : broadcastAddresses) {
+ memcpy(peerInfo.peer_addr, address.data(), 6);
+ peerInfo.channel = wifi_channel;
+ peerInfo.encrypt = false;
+
+ if (esp_now_add_peer(&peerInfo) != ESP_OK) {
+ Serial.println("ESP-NOW peer add failed");
+ }
}
}
-void sendESPNow(int mode) {
- myData.sima_start = mode;
- esp_err_t result = esp_now_send(broadcastAddress, (uint8_t*)&myData, sizeof(myData));
- Serial.println(result == ESP_OK ? "ESP-NOW Success" : "ESP-NOW Error");
+void sendESPNow(int data, int addressIndex) {
+ myData.sima_start = data;
+ const uint8_t* address = broadcastAddresses[addressIndex].data();
+ esp_err_t result = esp_now_send(address, (uint8_t*)&myData, sizeof(myData));
+ // Serial.println(result == ESP_OK ? " Success" : " Error");
+}
+
+// Overload with single parameter for backward compatibility
+void sendESPNow(int data) {
+ // By default, send to the first device in the broadcast list
+ sendESPNow(data, 0);
}
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/comm/espnow_comm.h b/app/esp_daemon/arduino/ESP_Daemon/lib/comm/espnow_comm.h
index e1eb2d8..8661155 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/comm/espnow_comm.h
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/comm/espnow_comm.h
@@ -1,13 +1,12 @@
#ifndef ESPNOW_COMM_H
#define ESPNOW_COMM_H
-#include
-
typedef struct {
int sima_start;
} struct_message;
void initESPNow();
-void sendESPNow(int mode);
+void sendESPNow(int data);
+void sendESPNow(int data, int addressIndex);
#endif
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/led_control/led_control.cpp b/app/esp_daemon/arduino/ESP_Daemon/lib/led_control/led_control.cpp
index 0324e1b..b127d7d 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/led_control/led_control.cpp
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/led_control/led_control.cpp
@@ -1,9 +1,13 @@
#include "led_control.h"
#include "config.h"
-#include
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
-volatile int mode = 0; // use volatile to ensure the variable is updated correctly in ISR
+// use volatile to ensure the variable is updated correctly in ISR
+volatile int mode = 0;
+volatile int sensor_mode = 0;
+unsigned long last_override_time = 0;
+const unsigned long override_duration = LED_OVR_DURATION;
+int current_mode;
void initLED() {
strip.begin();
@@ -19,6 +23,28 @@ void colorWipe(uint32_t color, int wait) {
}
}
+void breathingEffect(uint32_t color, int cycles) {
+ for (int cycle = 0; cycle < cycles; cycle++) {
+ for (int brightness = 0; brightness <= 250; brightness += 25) {
+ strip.setBrightness(brightness);
+ for (int i = 0; i < strip.numPixels(); i++) {
+ strip.setPixelColor(i, color);
+ }
+ strip.show();
+ delay(5);
+ }
+ for (int brightness = 250; brightness >= 0; brightness -= 25) {
+ strip.setBrightness(brightness);
+ for (int i = 0; i < strip.numPixels(); i++) {
+ strip.setPixelColor(i, color);
+ }
+ strip.show();
+ delay(5);
+ }
+ }
+ strip.setBrightness(LED_BRIGHTNESS); // Reset to default brightness
+}
+
void rainbow(int wait){
for (long firstPixelHue = 0; firstPixelHue < 5 * 65536; firstPixelHue += 256) {
strip.rainbow(firstPixelHue);
@@ -27,19 +53,105 @@ void rainbow(int wait){
}
}
+void colorWipeNonBlocking(uint32_t color, int wait) {
+ static int pixelIndex = 0;
+ static unsigned long lastUpdate = 0;
+ static bool wipeOn = true;
+
+ if (millis() - lastUpdate >= wait) {
+ strip.setBrightness(LED_BRIGHTNESS); // Reset to default brightness
+ if (wipeOn) {
+ strip.setPixelColor(pixelIndex, color);
+ } else {
+ strip.setPixelColor(pixelIndex, 0);
+ }
+ strip.show();
+ pixelIndex++;
+
+ if (pixelIndex >= strip.numPixels()) {
+ pixelIndex = 0;
+ wipeOn = !wipeOn;
+ }
+ lastUpdate = millis();
+ }
+}
+
+void breathingEffectNonBlocking(uint32_t color, int cycles) {
+ static int brightness = 0;
+ static int direction = 5; // Increased step size for faster transition
+ static int cycleCount = 0;
+
+ brightness += direction;
+
+ if (brightness >= 255 || brightness <= 0) {
+ direction = -direction;
+ if (brightness <= 0) {
+ cycleCount++;
+ if (cycleCount >= cycles) {
+ cycleCount = 0;
+ brightness = 0;
+ direction = 5;
+ return;
+ }
+ }
+ }
+
+ strip.setBrightness(brightness);
+ for (int i = 0; i < strip.numPixels(); i++) {
+ strip.setPixelColor(i, color);
+ }
+ strip.show();
+}
+
+void rainbowNonBlocking(int wait) {
+ static long firstPixelHue = 0;
+ static unsigned long lastUpdate = 0;
+
+ if (millis() - lastUpdate >= wait) {
+ strip.setBrightness(LED_BRIGHTNESS); // Reset to default brightness
+ strip.rainbow(firstPixelHue);
+ strip.show();
+ firstPixelHue += 256;
+ if (firstPixelHue >= 5 * 65536) {
+ firstPixelHue = 0;
+ }
+ lastUpdate = millis();
+ }
+}
+
void LEDTask(void* pvParameters) {
for (;;) {
- switch (mode) {
- case 2:
- colorWipe(strip.Color(0, 0, 255), 50);
- colorWipe(strip.Color(0, 0, 0), 50);
+ if (mode == SIMA_CMD || mode == EME_ENABLE || mode == EME_DISABLE) {
+ if (millis() - last_override_time > override_duration) {
+ mode = -1;
+ }
+ }
+
+ if (sensor_mode == BATT_LOW || sensor_mode == BATT_DISCONNECTED) {
+ current_mode = sensor_mode;
+ } else {
+ current_mode = (mode == -1) ? sensor_mode : mode;
+ }
+
+ switch (current_mode) {
+ case SIMA_CMD:
+ breathingEffectNonBlocking(strip.Color(255, 255, 0), 5);
+ break;
+ case EME_DISABLE:
+ breathingEffectNonBlocking(strip.Color(255, 0, 0), 5);
+ break;
+ case EME_ENABLE:
+ breathingEffectNonBlocking(strip.Color(0, 255, 0), 5);
+ break;
+ case BATT_DISCONNECTED:
+ colorWipeNonBlocking(strip.Color(0, 0, 255), 50);
break;
- case 1:
- colorWipe(strip.Color(255, 0, 0), 50);
- colorWipe(strip.Color(0, 0, 0), 50);
+ case BATT_LOW:
+ colorWipeNonBlocking(strip.Color(255, 0, 0), 50);
break;
default:
- rainbow(1);
+ rainbowNonBlocking(5); // Adjusted wait for smoother rainbow
}
+ vTaskDelay(1); // Yield to other tasks
}
}
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/led_control/led_control.h b/app/esp_daemon/arduino/ESP_Daemon/lib/led_control/led_control.h
index d53f817..31a4c68 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/led_control/led_control.h
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/led_control/led_control.h
@@ -3,8 +3,20 @@
#include
+enum led_mode {
+ DEFAULT_MODE,
+ BATT_LOW,
+ BATT_DISCONNECTED,
+ EME_ENABLE,
+ EME_DISABLE,
+ SIMA_CMD
+};
+
extern Adafruit_NeoPixel strip;
extern volatile int mode;
+extern volatile int sensor_mode;
+extern unsigned long last_override_time;
+extern const unsigned long override_duration;
void initLED();
void colorWipe(uint32_t color, int wait);
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/ros_node/ros_node.cpp b/app/esp_daemon/arduino/ESP_Daemon/lib/ros_node/ros_node.cpp
index 936c994..f41568c 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/ros_node/ros_node.cpp
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/ros_node/ros_node.cpp
@@ -44,28 +44,42 @@ void timer_callback(rcl_timer_t* timer, int64_t last_call_time) {
void sima_callback(const void* msgin) {
const std_msgs__msg__Int16* incoming = (const std_msgs__msg__Int16*)msgin;
+ int data = incoming->data;
- sendESPNow(incoming->data);
- mode = incoming->data; // For Testing
+ if (data >= 10) {
+ // Double-digit: tens digit is address index, ones digit is data
+ int addressIndex = data / 10 - 1;
+ int dataToSend = data % 10;
+ sendESPNow(dataToSend, addressIndex);
+ } else {
+ // Single-digit: send to all addresses
+ for (int i = 0; i < 4; i++) { // Assuming 4 addresses (SIMA_01 to SIMA_04)
+ sendESPNow(data, i);
+ }
+ }
+
+ mode = SIMA_CMD;
+ last_override_time = millis();
}
void emergency_callback(const void* msgin) {
const std_msgs__msg__Bool* incoming = (const std_msgs__msg__Bool*)msgin;
- if (incoming->data) digitalWrite(RELAY_PIN, HIGH);
- else digitalWrite(RELAY_PIN, LOW);
+ if (incoming->data) { digitalWrite(RELAY_PIN, ENABLE); mode = EME_ENABLE; }
+ else { digitalWrite(RELAY_PIN, DISABLE); mode = EME_DISABLE; }
+ last_override_time = millis();
}
// Free the resources allocated by micro-ROS
void destroy_entities() {
- rmw_context_t * rmw_context = rcl_context_get_rmw_context(&support.context);
- (void) rmw_uros_set_context_entity_destroy_session_timeout(rmw_context, 0);
+ rmw_context_t * rmw_context = rcl_context_get_rmw_context(&support.context);
+ (void) rmw_uros_set_context_entity_destroy_session_timeout(rmw_context, 0);
- rcl_timer_fini(&timer);
- rclc_executor_fini(&executor);
- rcl_init_options_fini(&init_options);
- rcl_node_fini(&node);
- rclc_support_fini(&support);
+ rcl_timer_fini(&timer);
+ rclc_executor_fini(&executor);
+ rcl_init_options_fini(&init_options);
+ rcl_node_fini(&node);
+ rclc_support_fini(&support);
rcl_publisher_fini(&counter_publisher, &node);
rcl_publisher_fini(&battery_voltage_publisher, &node);
@@ -123,7 +137,7 @@ void initROS() {
set_microros_serial_transports(Serial);
pinMode(RELAY_PIN, OUTPUT);
- digitalWrite(RELAY_PIN, LOW); // Ensure relay is off initially
+ digitalWrite(RELAY_PIN, RELAY_INITIAL_STATE);
sima_command_msg.data = 0;
counter_msg.data = 0;
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/sensors/voltmeter.cpp b/app/esp_daemon/arduino/ESP_Daemon/lib/sensors/voltmeter.cpp
index 4c2a9ca..931a5a1 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/sensors/voltmeter.cpp
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/sensors/voltmeter.cpp
@@ -1,6 +1,9 @@
#include "voltmeter.h"
-#include "ros_node.h"
#include "config.h"
+
+#include "ros_node.h"
+#include "led_control.h"
+
#include
#include
@@ -41,9 +44,9 @@ String getSensorReadings() {
readings["sensor"] = String(Vbattf);
readings["GND"] = 0;
- if (Vbattf < 3) readings["batteryStatus"] = "DISCONNECTED";
- else if (Vbattf < 17.5) readings["batteryStatus"] = "LOW";
- else readings["batteryStatus"] = "NORMAL";
+ if (Vbattf < 3) { readings["batteryStatus"] = "DISCONNECTED"; sensor_mode = BATT_DISCONNECTED; }
+ else if (Vbattf < 17.5) { readings["batteryStatus"] = "LOW"; sensor_mode = BATT_LOW; }
+ else { readings["batteryStatus"] = "NORMAL"; sensor_mode = DEFAULT_MODE; }
switch (state) {
case WAITING_AGENT: readings["microROS"] = "WAITING_AGENT"; break;
@@ -68,6 +71,6 @@ void voltmeterTask(void* pvParameters) {
events.send(getSensorReadings().c_str(), "new_readings", millis());
}
- delay(100);
+ vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/web_server/web_server.cpp b/app/esp_daemon/arduino/ESP_Daemon/lib/web_server/web_server.cpp
index c43ae6d..2626636 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/web_server/web_server.cpp
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/web_server/web_server.cpp
@@ -1,8 +1,11 @@
#include "web_server.h"
+#include "config.h"
+
#include "ros_node.h" // Include for emergency_msg
-#include
+
#include
#include
+#include
#include
AsyncWebServer server(80);
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/wifi_config/wifi_config.cpp b/app/esp_daemon/arduino/ESP_Daemon/lib/wifi_config/wifi_config.cpp
index 4c0cfef..78485c7 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/wifi_config/wifi_config.cpp
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/wifi_config/wifi_config.cpp
@@ -1,12 +1,37 @@
#include "wifi_config.h"
#include "config.h"
-#include
-#include
+
#include
+#include
+#include
+
+int wifi_channel = 0;
void initWiFi() {
WiFiManager wifiManager;
- wifiManager.autoConnect(HOSTNAME);
+ wifiManager.setEnableConfigPortal(false);
+
+ const int maxRetries = 10;
+ int retryCount = 0;
+ bool connected = false;
+
+ while (retryCount < maxRetries && !connected) {
+ Serial.printf("Attempting to connect to WiFi (%d/%d)...\n", retryCount + 1, maxRetries);
+ if (wifiManager.autoConnect(HOSTNAME)) {
+ connected = true;
+ Serial.println("WiFi connected!");
+ } else {
+ Serial.println("WiFi connection failed, retrying...");
+ retryCount++;
+ delay(3000);
+ }
+ }
+
+ if (!connected) {
+ Serial.println("Entering WiFi configuration portal...");
+ wifiManager.setEnableConfigPortal(true);
+ wifiManager.startConfigPortal(HOSTNAME);
+ }
Serial.println(WiFi.localIP());
@@ -15,4 +40,5 @@ void initWiFi() {
} else {
Serial.println("mDNS started");
}
+ wifi_channel = WiFi.channel();
}
diff --git a/app/esp_daemon/arduino/ESP_Daemon/lib/wifi_config/wifi_config.h b/app/esp_daemon/arduino/ESP_Daemon/lib/wifi_config/wifi_config.h
index 2d8d732..773d1e7 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/lib/wifi_config/wifi_config.h
+++ b/app/esp_daemon/arduino/ESP_Daemon/lib/wifi_config/wifi_config.h
@@ -3,4 +3,6 @@
void initWiFi();
+extern int wifi_channel;
+
#endif
diff --git a/app/esp_daemon/arduino/ESP_Daemon/src/main.cpp b/app/esp_daemon/arduino/ESP_Daemon/src/main.cpp
index ed3d4e1..9a43ed2 100644
--- a/app/esp_daemon/arduino/ESP_Daemon/src/main.cpp
+++ b/app/esp_daemon/arduino/ESP_Daemon/src/main.cpp
@@ -28,7 +28,6 @@ void microROSTask(void* pvParameters) {
EXECUTE_EVERY_N_MS(MROS_PING_INTERVAL, state = (RMW_RET_OK == rmw_uros_ping_agent(MROS_TIMEOUT_MS, 1)) ? AGENT_CONNECTED : AGENT_DISCONNECTED;);
if (state == AGENT_CONNECTED) {
rclc_executor_spin_some(&executor, RCL_MS_TO_NS(ROS_TIMER_MS));
- // vTaskDelay(10 / portTICK_PERIOD_MS);
}
break;
case AGENT_DISCONNECTED:
@@ -38,6 +37,7 @@ void microROSTask(void* pvParameters) {
default:
break;
}
+ // vTaskDelay(5 / portTICK_PERIOD_MS);
}
}
@@ -53,14 +53,13 @@ void setup() {
initLED();
initVoltmeter();
- xTaskCreatePinnedToCore(LEDTask, "LED Task", 10000, NULL, 1, &Task1, 0);
- xTaskCreatePinnedToCore(voltmeterTask, "Sensor Task", 10000, NULL, 1, &Task2, 1);
- xTaskCreatePinnedToCore(microROSTask, "microROS", 10000, NULL, 1, NULL, 1);
+ xTaskCreatePinnedToCore(LEDTask, "LED Task", 10000, NULL, 1, &Task1, 0);
+ xTaskCreatePinnedToCore(voltmeterTask, "Sensor Task", 10000, NULL, 1, &Task2, 0);
+ xTaskCreatePinnedToCore(microROSTask, "microROS", 10000, NULL, 1, NULL, 0);
- initESPNow();
-
initSPIFFS();
initWiFi();
+ initESPNow();
initWebServer();
}
diff --git a/app/robot_status_bridge/.env b/app/robot_status_bridge/.env
index 17fff1a..27af964 100644
--- a/app/robot_status_bridge/.env
+++ b/app/robot_status_bridge/.env
@@ -1,6 +1,6 @@
-
-# COMPOSE_BUILDER=bake
+# Docker Compose Settings
+COMPOSE_PROJECT_NAME=robot_status_bridge
COMPOSE_BAKE=true
+# ROS Settings
ROS_DISTRO=humble
-# ROS_DOMAIN_ID=25
\ No newline at end of file
diff --git a/app/robot_status_bridge/robot_status_br/src/robot_status_monitor/launch/robot_status.launch b/app/robot_status_bridge/robot_status_br/src/robot_status_monitor/launch/robot_status.launch
index 0a8318e..c284d4a 100644
--- a/app/robot_status_bridge/robot_status_br/src/robot_status_monitor/launch/robot_status.launch
+++ b/app/robot_status_bridge/robot_status_br/src/robot_status_monitor/launch/robot_status.launch
@@ -1,7 +1,7 @@
diff --git a/app/robot_status_bridge/robot_status_br/src/robot_status_monitor/src/robot_status_node.cpp b/app/robot_status_bridge/robot_status_br/src/robot_status_monitor/src/robot_status_node.cpp
index 7da70f3..f5846b0 100644
--- a/app/robot_status_bridge/robot_status_br/src/robot_status_monitor/src/robot_status_node.cpp
+++ b/app/robot_status_bridge/robot_status_br/src/robot_status_monitor/src/robot_status_node.cpp
@@ -75,7 +75,7 @@ class RobotStatusPublisher : public rclcpp::Node {
{"/robot_status/usb/mission", {"std_msgs::msg::Bool", "[ -e /dev/mission ] && echo 1 || echo 0" }},
{"/robot_status/usb/chassis", {"std_msgs::msg::Bool", "[ -e /dev/chassis ] && echo 1 || echo 0" }},
{"/robot_status/usb/lidar", {"std_msgs::msg::Bool", "[ -e /dev/lidar ] && echo 1 || echo 0" }},
- {"/robot_status/usb/esp", {"std_msgs::msg::Bool", "[ -e /dev/esp ] && echo 1 || echo 0" }},
+ {"/robot_status/usb/esp", {"std_msgs::msg::Bool", "[ -e /dev/esp-daemon ] && echo 1 || echo 0" }},
{"/robot_status/usb/imu", {"std_msgs::msg::Bool", "lsusb | grep -E '06c2:00[3-a][0-f]' > /dev/null && echo 1 || echo 0" }},
};
diff --git a/desktop/ESP-Daemon.desktop b/desktop/ESP-Daemon.desktop
new file mode 100755
index 0000000..7184203
--- /dev/null
+++ b/desktop/ESP-Daemon.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Type=Application
+Name=ESP-Daemon
+Exec=sh -c 'firefox "http://$(hostname | tr '[:upper:]' '[:lower:]')-esp.local"'
+Icon=/home/ditrobotics/DIT-Scripts/dit/ESP-Daemon.png
+Terminal=false
+Comment=Launch DIT script
+
diff --git a/dit/ESP-Daemon.png b/dit/ESP-Daemon.png
new file mode 100644
index 0000000..d367db1
Binary files /dev/null and b/dit/ESP-Daemon.png differ
diff --git a/share/scripts/main.run b/share/scripts/main.run
index 832324c..de23acd 100755
--- a/share/scripts/main.run
+++ b/share/scripts/main.run
@@ -4,7 +4,7 @@
tmux new-session -d -s eurobot-script-main
# Run main program
-tmux send-keys -t 0 "echo 'ditrobotics' | sudo -S docker compose -p main-run -f /home/main/Eurobot-2025-Main/docker/docker-compose.yaml up" C-m
+tmux send-keys -t 0 "echo 'ditrobotics' | sudo -S -u main /home/main/Eurobot-2025-Main/main run" C-m
# Attach to the session
tmux attach-session -d
diff --git a/share/scripts/remove.sh b/share/scripts/remove.sh
index 988a5a6..a962fcd 100755
--- a/share/scripts/remove.sh
+++ b/share/scripts/remove.sh
@@ -9,7 +9,7 @@ tmux send-keys -t 0 "echo 'ditrobotics' | sudo -S docker compose -p localization
# Remove navigation
tmux send-keys -t 0 "echo 'ditrobotics' | sudo -S docker compose -p navigation-run -f /home/navigation/Eurobot-2025-machine-ws/src/Eurobot-2025-Navigation2-envs/Navigation2-humble-deploy/docker-compose.yaml down" C-m
# Remove main
-tmux send-keys -t 0 "echo 'ditrobotics' | sudo -S docker compose -p main-run -f /home/main/Eurobot-2025-Main/docker/docker-compose.yaml down" C-m
+tmux send-keys -t 0 "echo 'ditrobotics' | sudo -S -u main /home/main/Eurobot-2025-Main/main close" C-m
# Attach to the session
tmux attach-session -d
diff --git a/share/scripts/rm-main.sh b/share/scripts/rm-main.sh
index 40b1b9d..de9b89c 100755
--- a/share/scripts/rm-main.sh
+++ b/share/scripts/rm-main.sh
@@ -4,7 +4,7 @@
tmux new-session -d -s eurobot-kill-script
# Remove main
-tmux send-keys -t 0 "echo 'ditrobotics' | sudo -S docker compose -p main-run -f /home/main/Eurobot-2025-Main/docker/docker-compose.yaml down" C-m
+tmux send-keys -t 0 "echo 'ditrobotics' | sudo -S -u main /home/main/Eurobot-2025-Main/main close" C-m
# Attach to the session
tmux attach-session -d
diff --git a/udev/80-dit.rules b/udev/80-dit.rules
index 536b191..ae39149 100644
--- a/udev/80-dit.rules
+++ b/udev/80-dit.rules
@@ -1,9 +1,11 @@
# DIT-2025-11
-ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="e40c7173d914ef11aba16db8bf9df066", SYMLINK+="chassis", MODE="0666"
-ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="b60451c2ae14ef11b0f075b8bf9df066", SYMLINK+="mission", MODE="0666"
+ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="ae17a2a6098eee1182ac01028acbdcd8", SYMLINK+="chassis", MODE="0666"
+ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="aa6549311a8eee119caafe018acbdcd8", SYMLINK+="mission", MODE="0666"
ACTION=="add", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="f450dedc3e77ed118311f6fafdf7b791", SYMLINK+="lidar", MODE="0666"
+ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", ATTRS{serial}=="64:E8:33:B6:57:88", SYMLINK+="esp-daemon", MODE="0666"
# DIT-2025-14
-ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="a83661c5e014ef118e9175b8bf9df066", SYMLINK+="chassis", MODE="0666"
-ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="3ca485551115ef11bc2c75b8bf9df066", SYMLINK+="mission", MODE="0666"
+ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="f2849de9b686ef11a45155405fd1dfbf", SYMLINK+="chassis", MODE="0666"
+ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="bc1fa6a7208eee11a33703028acbdcd8", SYMLINK+="mission", MODE="0666"
ACTION=="add", ATTRS{idVendor}=="10c4", ATTRS{idProduct}=="ea60", ATTRS{serial}=="e8ddfc52f40aee11b2654502f59e3369", SYMLINK+="lidar", MODE="0666"
+ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="303a", ATTRS{idProduct}=="1001", ATTRS{serial}=="64:E8:33:B6:57:88", SYMLINK+="esp-daemon", MODE="0666"
diff --git a/web/docker/.env b/web/docker/.env
index 0be2fd5..c11e629 100644
--- a/web/docker/.env
+++ b/web/docker/.env
@@ -1 +1,6 @@
-ROS_DISTRO=humble
\ No newline at end of file
+# Docker Compose Settings
+COMPOSE_PROJECT_NAME=foxglove-bridge
+COMPOSE_BAKE=true
+
+# ROS Settings
+ROS_DISTRO=humble