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

+
+
+
+ + + +
+
+ + + + + +
+ Control 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