Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/IBT2MotorDemo/IBT2MotorDemo.ino
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
*/

#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
1 change: 1 addition & 0 deletions examples/JoystickTest/JoystickTest.ino
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
*/

#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
1 change: 1 addition & 0 deletions examples/MotorOpenLoopDemo/MotorOpenLoopDemo.ino
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
*/

#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
1 change: 1 addition & 0 deletions examples/command_based/AutonomousDemo/AutonomousDemo.ino
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
1 change: 1 addition & 0 deletions examples/command_based/ShooterDemo/ShooterDemo.ino
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
1 change: 1 addition & 0 deletions examples/command_based/SliderDemo/SliderDemo.ino
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
1 change: 1 addition & 0 deletions examples/command_based/TankDriveDemo/TankDriveDemo.ino
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
1 change: 1 addition & 0 deletions examples/command_based/TurretDemo/TurretDemo.ino
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#define PROBOT_WIFI_AP_PASSWORD "ProBot1234"
#define PROBOT_WIFI_AP_CHANNEL 3 // Opsiyonel (varsayilan 1)

#include <probot.h>
#include <probot/io/joystick_api.hpp>
Expand Down
52 changes: 50 additions & 2 deletions src/driverstation/esp32s3/driver_station_esp32.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
#error "DriverStation AP password not provided. Define PROBOT_WIFI_AP_PASSWORD (>=8 chars) before including probot.h."
#endif
static_assert(sizeof(PROBOT_WIFI_AP_PASSWORD) - 1 >= 8, "PROBOT_WIFI_AP_PASSWORD must be at least 8 characters.");
#ifdef PROBOT_WIFI_AP_SSID
static_assert(sizeof(PROBOT_WIFI_AP_SSID) - 1 >= 1, "PROBOT_WIFI_AP_SSID must be at least 1 character.");
#ifdef PROBOT_WIFI_AP_SSID_NO_MAC_SUFFIX
static_assert(sizeof(PROBOT_WIFI_AP_SSID) - 1 <= 32, "PROBOT_WIFI_AP_SSID must be 32 characters or fewer.");
#else
static_assert(sizeof(PROBOT_WIFI_AP_SSID) - 1 <= 25, "PROBOT_WIFI_AP_SSID must be 25 characters or fewer when MAC suffix is enabled.");
#endif
#endif
#ifndef PROBOT_WIFI_AP_CHANNEL
#define PROBOT_WIFI_AP_CHANNEL 1
#endif
static_assert(PROBOT_WIFI_AP_CHANNEL >= 1 && PROBOT_WIFI_AP_CHANNEL <= 11,
"PROBOT_WIFI_AP_CHANNEL must be between 1 and 11.");

namespace probot::driverstation::esp32 {
class DriverStation {
Expand All @@ -21,23 +34,36 @@ namespace probot::driverstation::esp32 {

void begin(){
const char* pw = PROBOT_WIFI_AP_PASSWORD;
String ssid = generateSSID();
String ssid;
#ifdef PROBOT_WIFI_AP_SSID
ssid = String(PROBOT_WIFI_AP_SSID);
#ifndef PROBOT_WIFI_AP_SSID_NO_MAC_SUFFIX
char suffix[8];
snprintf(suffix, sizeof(suffix), "-%06X", (unsigned int)(ESP.getEfuseMac() & 0xFFFFFF));
ssid += suffix;
#endif
#else
ssid = generateSSID();
#endif
ap_ssid_ = ssid;
WiFi.mode(WIFI_AP);
WiFi.softAP(ssid.c_str(), pw);
WiFi.softAP(ssid.c_str(), pw, PROBOT_WIFI_AP_CHANNEL);

Serial.println("[DS ] ========================================");
Serial.print("[DS ] WiFi SSID: ");
Serial.println(ssid);
Serial.print("[DS ] Password: ");
Serial.println("********");
Serial.print("[DS ] Channel: ");
Serial.println(PROBOT_WIFI_AP_CHANNEL);
Serial.print("[DS ] IP Address: ");
Serial.println(WiFi.softAPIP());
Serial.println("[DS ] ========================================");

_server.on("/", HTTP_GET, [this](){ if (!enforceOwner()) return; handleRoot(); });
_server.on("/updateController", HTTP_POST, [this](){ if (!enforceOwner()) return; handleUpdateController(); });
_server.on("/robotControl", HTTP_GET, [this](){ if (!enforceOwner()) return; handleRobotControl(); });
_server.on("/getState", HTTP_GET, [this](){ if (!enforceOwner()) return; handleGetState(); });
_server.on("/getBattery", HTTP_GET, [this](){ handleGetBattery(); });
_server.on("/telemetry", HTTP_GET, [this](){ if (!enforceOwner()) return; handleTelemetry(); });
_server.begin();
Expand Down Expand Up @@ -126,6 +152,26 @@ namespace probot::driverstation::esp32 {
_server.send(200, "text/plain", buf);
}

void handleGetState(){
auto s = _rs.read();
uint32_t now_ms = millis();
uint32_t remaining_ms = 0;
if (s.phase == probot::robot::Phase::AUTONOMOUS && s.autonomousEnabled &&
s.autoStartMs != 0 && s.autoPeriodSeconds > 0) {
uint32_t total_ms = static_cast<uint32_t>(s.autoPeriodSeconds) * 1000u;
uint32_t elapsed = now_ms - s.autoStartMs;
remaining_ms = (elapsed >= total_ms) ? 0u : (total_ms - elapsed);
}
char buf[128];
snprintf(buf, sizeof(buf),
"{\"phase\":%u,\"autonomousEnabled\":%s,\"autoPeriodSeconds\":%d,\"autoRemainingMs\":%u}",
static_cast<unsigned>(s.phase),
s.autonomousEnabled ? "true" : "false",
(int)s.autoPeriodSeconds,
(unsigned)remaining_ms);
_server.send(200, "application/json", buf);
}

void handleRobotControl(){
String cmd = _server.arg("cmd");
bool enAuto = _server.arg("auto").toInt() != 0;
Expand All @@ -137,6 +183,8 @@ namespace probot::driverstation::esp32 {
_rs.setAutonomous(millis(), enAuto);
if (autoLen > 0) _rs.setAutoPeriodSeconds(millis(), autoLen);
_rs.setStatus(millis(), robot::Status::START);
} else if (cmd == "cancelAuto"){
_rs.setAutonomous(millis(), false);
} else if (cmd == "stop"){
_rs.setStatus(millis(), robot::Status::STOP);
}
Expand Down
120 changes: 117 additions & 3 deletions src/driverstation/esp32s3/index_html.h
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@ const char MAIN_page[] PROGMEM = R"=====(
<h2>Telemetry</h2>
<pre id="telemetryOutput" style="height:150px;overflow-y:auto;background:rgba(0,32,77,0.05);padding:12px;border-radius:12px;font-size:0.9rem;"></pre>
<button onclick="clearTelemetry()" style="margin-top:12px;padding:10px 20px;font-size:0.9rem;">Clear</button>
<button id="autoScrollToggle" onclick="toggleAutoScroll()" style="margin-top:8px;padding:10px 20px;font-size:0.9rem;">Auto-scroll: ON</button>
</section>
<section class="stack-card telemetry" id="logs">
<h2>System Logs</h2>
Expand All @@ -720,6 +721,7 @@ const char MAIN_page[] PROGMEM = R"=====(
<script>
let controlState="idle";
let autoModeEnabled=false;
let autoScroll=true;
let selectedGamepadIndex=-1;
let gamepads={};
let gamepadDetected=false;
Expand Down Expand Up @@ -826,6 +828,85 @@ function stopAutoTimer(){
updateAutoDisplay();
}

async function syncState(){
try{
const r = await fetch('/getState');
if(!r.ok) return;
const data = await r.json();
const btn = document.getElementById('robotButton');
if(!btn) return;

const autoPeriodEl = document.getElementById('autoPeriod');
const autoEnableEl = document.getElementById('enableAutonomous');

const isAutonomous = (data.phase === 2);
const isTeleop = (data.phase === 3);
const isRunning = (isAutonomous || isTeleop);
if(autoPeriodEl && typeof data.autoPeriodSeconds === 'number' && isRunning){
autoPeriodEl.value = data.autoPeriodSeconds;
}
if(autoEnableEl){
if(typeof data.autonomousEnabled === 'boolean' && (isAutonomous || isTeleop)){
autoEnableEl.checked = data.autonomousEnabled;
}
autoEnableEl.disabled = isTeleop;
}

const remainingMs = (typeof data.autoRemainingMs === 'number') ? data.autoRemainingMs : null;
const remainingSec = remainingMs !== null ? Math.max(0, remainingMs) / 1000 : (parseFloat(autoPeriodEl ? autoPeriodEl.value : 0) || 0);

if(data.phase === 1){
controlState = "armed";
btn.textContent = "Start";
btn.style.background = "var(--start)";
btn.style.color = "var(--ice)";
stopAutoTimer();
if(autoEnableEl && autoEnableEl.checked){
autoRemaining = parseFloat(autoPeriodEl ? autoPeriodEl.value : 0) || 0;
}else{
autoRemaining = 0;
}
updateAutoDisplay();
setPhaseDisplay('init');
}else if(data.phase === 2){
controlState = "running";
btn.textContent = "Stop";
btn.style.background = "var(--stop)";
btn.style.color = "var(--ice)";
if(data.autonomousEnabled === false){
stopAutoTimer();
setPhaseDisplay('teleop');
}else{
stopAutoTimer();
if(remainingSec > 0){
startAutoTimer(remainingSec);
}else{
autoModeEnabled = true;
autoRemaining = 0;
updateAutoDisplay();
setPhaseDisplay('auto');
}
}
}else if(data.phase === 3){
controlState = "running";
btn.textContent = "Stop";
btn.style.background = "var(--stop)";
btn.style.color = "var(--ice)";
stopAutoTimer();
setPhaseDisplay('teleop');
}else{
controlState = "idle";
btn.textContent = "Init";
btn.style.background = "var(--navy)";
btn.style.color = "var(--ice)";
stopAutoTimer();
setPhaseDisplay('stopped');
}
}catch(e){
console.error('syncState failed:', e);
}
}

async function handleRobotButton(){
let cmd="";
const enableAuto=document.getElementById('enableAutonomous').checked;
Expand Down Expand Up @@ -973,10 +1054,12 @@ function stopAutoTimer(){
window.addEventListener('load',()=>{
ensureJoyButtons(DEFAULT_BUTTON_COUNT);
updateJoyVisuals(null);
updateAutoScrollButton();
autoRemaining=parseFloat(document.getElementById('autoPeriod').value)||0;
updateAutoDisplay();
setPhaseDisplay('standby');
requestAnimationFrame(gamepadLoop);
syncState();
});

document.getElementById('autoPeriod').addEventListener('input',e=>{
Expand All @@ -989,9 +1072,21 @@ function stopAutoTimer(){
updateAutoDisplay();
});

document.getElementById('enableAutonomous').addEventListener('change',e=>{
document.getElementById('enableAutonomous').addEventListener('change', async e=>{
if(!e.target.checked){
stopAutoTimer();
if(controlState === "running"){
try{
const r = await fetch('/robotControl?cmd=cancelAuto');
if(!r.ok) throw new Error("Cancel auto failed");
}catch(err){
console.error(err);
return;
}
stopAutoTimer();
setPhaseDisplay('teleop');
}else{
stopAutoTimer();
}
}
});

Expand All @@ -1002,15 +1097,34 @@ function stopAutoTimer(){
if(r.ok){
const text=await r.text();
const el=document.getElementById('telemetryOutput');
if(el && text) el.textContent=text;
if(el && text){
el.textContent=text;
if(autoScroll){
el.scrollTop = el.scrollHeight;
}
}
}
}catch(e){}
}
function clearTelemetry(){
const el=document.getElementById('telemetryOutput');
if(el) el.textContent='';
}
function updateAutoScrollButton(){
const btn=document.getElementById('autoScrollToggle');
if(!btn) return;
btn.textContent = autoScroll ? 'Auto-scroll: ON' : 'Auto-scroll: OFF';
}
function toggleAutoScroll(){
autoScroll = !autoScroll;
updateAutoScrollButton();
if(autoScroll){
const el=document.getElementById('telemetryOutput');
if(el) el.scrollTop = el.scrollHeight;
}
}
setInterval(pollTelemetry,50);
setInterval(syncState, 1000);
</script>
</body>
</html>
Expand Down
12 changes: 11 additions & 1 deletion src/probot/core/runtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ namespace probot {
inline RuntimeState g_state{};

inline void autonomousWorker(void*){
__atomic_store_n(&g_state.auto_start_ms, millis(), __ATOMIC_SEQ_CST);
uint32_t now = millis();
__atomic_store_n(&g_state.auto_start_ms, now, __ATOMIC_SEQ_CST);
probot::robot::state().setAutoStartMs(now, now);
::autonomousInit();
for(;;){ ::autonomousLoop(); vTaskDelay(pdMS_TO_TICKS(20)); }
}
Expand Down Expand Up @@ -64,6 +66,7 @@ namespace probot {
auto& s = g_state;
if (s.hAuto){ vTaskDelete(s.hAuto); s.hAuto = nullptr; }
__atomic_store_n(&s.auto_start_ms, 0u, __ATOMIC_SEQ_CST);
probot::robot::state().setAutoStartMs(millis(), 0u);
}

inline void stopTeleop(){
Expand Down Expand Up @@ -177,6 +180,7 @@ namespace probot {
if (s.autonomousEnabled){
autoLen = s.autoPeriodSeconds;
__atomic_store_n(&detail::g_state.auto_start_ms, 0u, __ATOMIC_SEQ_CST);
probot::robot::state().setAutoStartMs(now, 0u);
probot::robot::state().setPhase(now, Phase::AUTONOMOUS);
startAutonomous();
} else {
Expand All @@ -203,6 +207,12 @@ namespace probot {
}
}

if (s.status == Status::START && s.phase == Phase::AUTONOMOUS && !s.autonomousEnabled){
stopAutonomous();
probot::robot::state().setPhase(now, Phase::TELEOP);
startTeleop();
}

if (now - lastLed >= 500){
lastLed = now;
updateLed();
Expand Down
Loading