5087819be23e59ffe302173d5465018cd6103f2e
Board-dat/SDR/SDR1117-dat/SDR1117-dat.md
| ... | ... | @@ -59,7 +59,7 @@ expanding PCB by [[PCB-accesories-dat]] |
| 59 | 59 | |
| 60 | 60 | ## setup |
| 61 | 61 | |
| 62 | -- [[CRSF-dat]] |
|
| 62 | +- [[CRSF-dat]] - [[radiomaster-pocket-dat]] |
|
| 63 | 63 | |
| 64 | 64 | correct to SoftwareSerial soft(13, 12); // RX, TX |
| 65 | 65 |
Board-dat/SDR/SDR1125-dat/SDR1125-dat.md
| ... | ... | @@ -2,6 +2,9 @@ |
| 2 | 2 | |
| 3 | 3 | # SDR1125-dat.md |
| 4 | 4 | |
| 5 | + |
|
| 6 | +- [[SDR1125-dat]] - [[CRSF-dat]] |
|
| 7 | + |
|
| 5 | 8 | orange boxes series |
| 6 | 9 | |
| 7 | 10 | - [[OPM1153-dat]] - [[MP1584-dat]] == orange middle module |
Chip-cn-dat/Espressif-dat/ESP8266-DAT/ESP8266-DAT.md
| ... | ... | @@ -23,33 +23,7 @@ hardware and software - [[ESP8266-HDK-dat]] - [[ESP8266-SDK-dat]] |
| 23 | 23 | debug - [[ESP8266-debug-log-dat]] - [[ESP8266-error-log-dat]] - [[ESP8266-boot-log-dat]]
|
| 24 | 24 | |
| 25 | 25 | |
| 26 | -## design
|
|
| 27 | -
|
|
| 28 | -software serial - can not be more than 115200 baud, otherwise the data will be corrupted.
|
|
| 29 | -
|
|
| 30 | -The data you shared is raw CRSF binary packets, but they look slightly corrupted or "jittery," which is very common when using SoftwareSerial at 115200 on an ESP8266.
|
|
| 31 | -
|
|
| 32 | -- [[CRSF-dat]]
|
|
| 33 | -
|
|
| 34 | - 0B 56 FE D8 10 0B 56 FE D8 10 0B 52 FE 84 D8 10
|
|
| 35 | - 0B 52 FE D8 10 0B 56 FE D8 10 0B 52 FF D8 10 0B
|
|
| 36 | - 52 FF D9 10 99 FF 44 D8 10 0B 56 FE D8 10 0B 52
|
|
| 37 | - FF D8 10 0B 56 FE D8 10 0B 56 FF 25 26 12 4B A2
|
|
| 38 | - D8 10 0B 52 FE D8 10 0B 56 FE A6 12 4B D8 10 99
|
|
| 39 | - FF D8 10 0B 56 FE E4 26 12 4B A2 A6 12 4B D8 10
|
|
| 40 | - 0B 52 FE D8 10 0B 56 FE D8 10 0B 56 FF 84 D8 10
|
|
| 41 | - 99 FF D8 10 0B 52 FF D8 10 0B 56 FE A6 12 4B A4
|
|
| 42 | - D8 10 0B 56 FE D8 10 FF D8 10 0B 52 FE D8 10 0B
|
|
| 43 | - 56 FE D8 10 0B 56 FE 44 12 4B FF D8 10 0B 52 FF
|
|
| 44 | - D8 10 0B 56 FF D9 10 FE D8 10 0B 56 FF 80 D8 10
|
|
| 45 | - 0B 56 FF D8 10 0B 56 FF 12 4B 54 FF 84 D8 10 0B
|
|
| 46 | - 52 FF D8 10 0B 52 FF D8 10 0B 52 FF D8 10 0B 56
|
|
| 47 | - FF D9 10 FE C4 D9 10 0B 56 FF D8 10 FE 12 4B 02
|
|
| 48 | - 4E D8 10 0B D2 FF D8 10 FE A2 D8 10 0B 52 FF D8
|
|
| 49 | - 10 0B 56 FF D8 10 FE D8 10 0B 56 FF 00 80 D8 10
|
|
| 50 | - 0B 56 FE D8 10 0B 56 FE D8 10 0B 52 FE D8 10 FF
|
|
| 51 | - D8 10 0B 56 FE 80 D8 10 0B 52 FE D8 10 0B 56 FE
|
|
| 52 | - D8 10 0B 52 FE D8 10 0B 52 FE D8 10 99 FF 40 D8
|
|
| 26 | +
|
|
| 53 | 27 | |
| 54 | 28 | ## ref
|
| 55 | 29 |
Network-dat/RC-dat/RC-protocols-dat/CRSF-dat/CRSF-dat.md
| ... | ... | @@ -1,6 +1,10 @@ |
| 1 | 1 | |
| 2 | 2 | # CRSF-dat
|
| 3 | 3 | |
| 4 | +
|
|
| 5 | +
|
|
| 6 | +
|
|
| 7 | +
|
|
| 4 | 8 | CRSF (Crossfire Serial Protocol) is a low-latency, high-speed serial protocol developed by **Team BlackSheep (TBS)** for communication between radio receivers (like TBS Crossfire Nano RX) and flight controllers.
|
| 5 | 9 | |
| 6 | 10 | It’s used in RC applications (especially FPV drones) to transmit RC channel data, telemetry, and link status over a compact serial format.
|
| ... | ... | @@ -8,7 +12,9 @@ It’s used in RC applications (especially FPV drones) to transmit RC channel da |
| 8 | 12 | CRSF packets are binary data. Here's the basic structure of a CRSF packet:
|
| 9 | 13 | |
| 10 | 14 | |
| 15 | +## board
|
|
| 11 | 16 | |
| 17 | +- [[SDR1125-dat]] - [[CRSF-dat]]
|
|
| 12 | 18 | |
| 13 | 19 | |
| 14 | 20 |
SDK-dat/ESP-SDK-dat/ESP8266-SDK-dat/ESP8266-SDK-dat.md
| ... | ... | @@ -1,6 +1,9 @@ |
| 1 | 1 | |
| 2 | 2 | # ESP8266-SDK-dat
|
| 3 | 3 | |
| 4 | +- [[esp866-code-dat]] - [[ESP8266-SDK-dat]] - [[ESP-dat]]
|
|
| 5 | +
|
|
| 6 | +
|
|
| 4 | 7 | - [[esp-idf-dat]] - [[ESP-dat]] - [[esp-sdk-dat]] - [[esptool-dat]]
|
| 5 | 8 | |
| 6 | 9 | - [[ESP8266-HDK-dat]]
|
code-dat/esp866-code-dat/ESP8266-WIFI-motor-1/ESP8266-WIFI-motor-1.ino
| ... | ... | @@ -0,0 +1,283 @@ |
| 1 | +#include <ESP8266WiFi.h> |
|
| 2 | +#include <ESP8266WebServer.h> |
|
| 3 | + |
|
| 4 | +// WiFi AP settings (fixed IP) |
|
| 5 | +const char *ssid = "MotorAP"; |
|
| 6 | +const char *password = "motorpass"; // set to "" for open AP |
|
| 7 | +IPAddress apIP(192, 168, 4, 1); |
|
| 8 | +IPAddress netMsk(255, 255, 255, 0); |
|
| 9 | + |
|
| 10 | +// Define pins for motor control |
|
| 11 | +// Motor 1 (Left) |
|
| 12 | +const int M1_IN1 = 4; |
|
| 13 | +const int M1_IN2 = 5; |
|
| 14 | +// Motor 2 (Right) |
|
| 15 | +const int M2_IN1 = 0; |
|
| 16 | +const int M2_IN2 = 2; |
|
| 17 | + |
|
| 18 | +int carSpeed = 200; // 0..255 (Base Speed) |
|
| 19 | +int minSpeed = 100; // Minimum speed to avoid stalling |
|
| 20 | +int turnDiff = 85; // Increased for more "agility" (sharper turns) |
|
| 21 | +int carAction = 0; // 0: stop, 1: forward, 2: backward, 3: left, 4: right |
|
| 22 | +unsigned long lastCommandTime = 0; |
|
| 23 | +const unsigned long SAFETY_TIMEOUT = 300; // Even tighter for high responsiveness |
|
| 24 | + |
|
| 25 | +int batteryS = 3; // Default 3S (11.1V) |
|
| 26 | + |
|
| 27 | +ESP8266WebServer server(80); |
|
| 28 | + |
|
| 29 | +int getCompensatedSpeed(int baseSpeed) { |
|
| 30 | + if (baseSpeed == 0) return 0; |
|
| 31 | + // Simple inverse scaling: |
|
| 32 | + // If it's 3S (11.1V), we scale DOWN the speed compared to 2S (7.4V) |
|
| 33 | + // Scale factor = 7.4 / 11.1 = ~0.66 |
|
| 34 | + if (batteryS == 3) { |
|
| 35 | + return (int)(baseSpeed * 0.66); |
|
| 36 | + } |
|
| 37 | + return baseSpeed; // Use raw speed for 2S |
|
| 38 | +} |
|
| 39 | + |
|
| 40 | +void applyMotorControl() |
|
| 41 | +{ |
|
| 42 | + int leftSpeed = 0; |
|
| 43 | + int rightSpeed = 0; |
|
| 44 | + |
|
| 45 | + int compensatedSpeed = getCompensatedSpeed(carSpeed); |
|
| 46 | + int compensatedTurnDiff = getCompensatedSpeed(turnDiff); |
|
| 47 | + |
|
| 48 | + // Helper to ensure motor gets enough power or is OFF |
|
| 49 | + auto normalizeSpeed = [&](int s) { |
|
| 50 | + if (s <= 0) return 0; |
|
| 51 | + if (s < minSpeed) return minSpeed; |
|
| 52 | + if (s > 255) return 255; |
|
| 53 | + return s; |
|
| 54 | + }; |
|
| 55 | + |
|
| 56 | + switch (carAction) |
|
| 57 | + { |
|
| 58 | + case 0: // Stop |
|
| 59 | + leftSpeed = rightSpeed = 0; |
|
| 60 | + break; |
|
| 61 | + case 1: // Forward |
|
| 62 | + leftSpeed = rightSpeed = compensatedSpeed; |
|
| 63 | + break; |
|
| 64 | + case 2: // Backward |
|
| 65 | + leftSpeed = rightSpeed = -compensatedSpeed; |
|
| 66 | + break; |
|
| 67 | + case 3: // Spin Left |
|
| 68 | + leftSpeed = -compensatedSpeed; |
|
| 69 | + rightSpeed = compensatedSpeed; |
|
| 70 | + break; |
|
| 71 | + case 4: // Spin Right |
|
| 72 | + leftSpeed = compensatedSpeed; |
|
| 73 | + rightSpeed = -compensatedSpeed; |
|
| 74 | + break; |
|
| 75 | + case 5: // Forward Left (Curve) - Swapped |
|
| 76 | + leftSpeed = compensatedSpeed + compensatedTurnDiff; |
|
| 77 | + rightSpeed = compensatedSpeed - compensatedTurnDiff; |
|
| 78 | + break; |
|
| 79 | + case 6: // Forward Right (Curve) - Swapped |
|
| 80 | + leftSpeed = compensatedSpeed - compensatedTurnDiff; |
|
| 81 | + rightSpeed = compensatedSpeed + compensatedTurnDiff; |
|
| 82 | + break; |
|
| 83 | + case 7: // Backward Left (Curve) |
|
| 84 | + leftSpeed = -(compensatedSpeed + compensatedTurnDiff); |
|
| 85 | + rightSpeed = -(compensatedSpeed - compensatedTurnDiff); |
|
| 86 | + break; |
|
| 87 | + case 8: // Backward Right (Curve) |
|
| 88 | + leftSpeed = -(compensatedSpeed - compensatedTurnDiff); |
|
| 89 | + rightSpeed = -(compensatedSpeed + compensatedTurnDiff); |
|
| 90 | + break; |
|
| 91 | + default: |
|
| 92 | + leftSpeed = rightSpeed = 0; |
|
| 93 | + break; |
|
| 94 | + } |
|
| 95 | + |
|
| 96 | + // Apply Left Motor |
|
| 97 | + int absL = abs(leftSpeed); |
|
| 98 | + int finalL = normalizeSpeed(absL); |
|
| 99 | + if (leftSpeed > 0) { |
|
| 100 | + analogWrite(M1_IN1, finalL); |
|
| 101 | + digitalWrite(M1_IN2, LOW); |
|
| 102 | + } else if (leftSpeed < 0) { |
|
| 103 | + digitalWrite(M1_IN1, LOW); |
|
| 104 | + analogWrite(M1_IN2, finalL); |
|
| 105 | + } else { |
|
| 106 | + digitalWrite(M1_IN1, LOW); |
|
| 107 | + digitalWrite(M1_IN2, LOW); |
|
| 108 | + } |
|
| 109 | + |
|
| 110 | + // Apply Right Motor |
|
| 111 | + int absR = abs(rightSpeed); |
|
| 112 | + int finalR = normalizeSpeed(absR); |
|
| 113 | + if (rightSpeed > 0) { |
|
| 114 | + analogWrite(M2_IN1, finalR); |
|
| 115 | + digitalWrite(M2_IN2, LOW); |
|
| 116 | + } else if (rightSpeed < 0) { |
|
| 117 | + digitalWrite(M2_IN1, LOW); |
|
| 118 | + analogWrite(M2_IN2, finalR); |
|
| 119 | + } else { |
|
| 120 | + digitalWrite(M2_IN1, LOW); |
|
| 121 | + digitalWrite(M2_IN2, LOW); |
|
| 122 | + } |
|
| 123 | +} |
|
| 124 | + |
|
| 125 | +String pageRoot() |
|
| 126 | +{ |
|
| 127 | + String html = "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">"; |
|
| 128 | + html += "<style>"; |
|
| 129 | + html += "body { font-family: sans-serif; text-align: center; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f0f0f0; }"; |
|
| 130 | + html += ".btn { width: 80px; height: 80px; margin: 10px; border-radius: 15px; border: none; background: #3498db; color: white; font-size: 24px; font-weight: bold; cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent; box-shadow: 0 4px #2980b9; }"; |
|
| 131 | + html += ".btn:active { background: #2980b9; box-shadow: 0 2px #1c5982; transform: translateY(2px); }"; |
|
| 132 | + html += ".stop-btn { background: #e74c3c; box-shadow: 0 4px #c0392b; }"; |
|
| 133 | + html += ".stop-btn:active { background: #c0392b; box-shadow: 0 2px #962d22; }"; |
|
| 134 | + html += ".row { display: flex; justify-content: center; }"; |
|
| 135 | + html += ".speed-container { margin-top: 20px; width: 80%; }"; |
|
| 136 | + html += "input[type=range] { width: 100%; height: 25px; cursor: pointer; }"; |
|
| 137 | + html += "select { padding: 8px; font-size: 16px; border-radius: 5px; }"; |
|
| 138 | + html += "</style></head><body>"; |
|
| 139 | + html += "<h2>Motor Control</h2>"; |
|
| 140 | + |
|
| 141 | + auto btnHtml = [&](int act, String icon) { |
|
| 142 | + String s_act = String(act); |
|
| 143 | + return " <button class='btn' onmousedown=\"setAction(" + s_act + ", event)\" ontouchstart=\"setAction(" + s_act + ", event)\" " |
|
| 144 | + "onmouseup=\"setAction(0, event)\" onmouseleave=\"setAction(0, event)\" ontouchend=\"setAction(0, event)\" ontouchcancel=\"setAction(0, event)\">" + icon + "</button>"; |
|
| 145 | + }; |
|
| 146 | + |
|
| 147 | + html += "<div class='row'>"; |
|
| 148 | + html += btnHtml(5, "↖"); |
|
| 149 | + html += btnHtml(1, "↑"); |
|
| 150 | + html += btnHtml(6, "↗"); |
|
| 151 | + html += "</div>"; |
|
| 152 | + |
|
| 153 | + html += "<div class='row'>"; |
|
| 154 | + html += btnHtml(3, "←"); |
|
| 155 | + html += " <button class='btn stop-btn' onclick=\"setAction(0, event)\" ontouchstart=\"setAction(0, event)\">OFF</button>"; |
|
| 156 | + html += btnHtml(4, "→"); |
|
| 157 | + html += "</div>"; |
|
| 158 | + |
|
| 159 | + html += "<div class='row'>"; |
|
| 160 | + html += btnHtml(7, "↙"); |
|
| 161 | + html += btnHtml(2, "↓"); |
|
| 162 | + html += btnHtml(8, "↘"); |
|
| 163 | + html += "</div>"; |
|
| 164 | + |
|
| 165 | + html += "<div class='speed-container'>"; |
|
| 166 | + html += " Speed: <span id='v'>" + String(carSpeed) + "</span>"; |
|
| 167 | + html += " <input type='range' id='s' min='100' max='255' value='" + String(carSpeed) + "' oninput='setSpeed(this.value)' />"; |
|
| 168 | + html += "</div>"; |
|
| 169 | + |
|
| 170 | + html += "<div class='speed-container'>"; |
|
| 171 | + html += " Battery Mode: <select onchange=\"setBattery(this.value)\">"; |
|
| 172 | + html += " <option value='2'" + String(batteryS == 2 ? " selected" : "") + ">2S (7.4V)</option>"; |
|
| 173 | + html += " <option value='3'" + String(batteryS == 3 ? " selected" : "") + ">3S (11.1V)</option>"; |
|
| 174 | + html += " </select>"; |
|
| 175 | + html += "</div>"; |
|
| 176 | + |
|
| 177 | + html += "<script>"; |
|
| 178 | + html += "let lastA = -1; let lastS = -1;"; |
|
| 179 | + html += "function setAction(a, e, f) { if(e) { e.preventDefault(); } if(!f && a == lastA && a != 0) return; lastA = a; fetch('/set?action=' + a).catch(e => console.error(e)); }"; |
|
| 180 | + html += "function setSpeed(s) { if(s == lastS) return; lastS = s; document.getElementById('v').innerText = s; fetch('/set?speed=' + s).catch(e => console.error(e)); }"; |
|
| 181 | + html += "function setBattery(b) { fetch('/set?batt=' + b); }"; |
|
| 182 | + html += "setInterval(() => { if(lastA > 0) setAction(lastA, null, true); }, 100);"; // "Pro" agility: 100ms heartbeat |
|
| 183 | + html += "</script></body></html>"; |
|
| 184 | + return html; |
|
| 185 | +} |
|
| 186 | + |
|
| 187 | +void handleRoot() |
|
| 188 | +{ |
|
| 189 | + server.send(200, "text/html", pageRoot()); |
|
| 190 | +} |
|
| 191 | + |
|
| 192 | +void handleSet() |
|
| 193 | +{ |
|
| 194 | + server.client().setNoDelay(true); // Disable Nagle's algorithm for faster response |
|
| 195 | + lastCommandTime = millis(); // Refresh safety watchdog |
|
| 196 | + if (server.hasArg("action")) |
|
| 197 | + { |
|
| 198 | + carAction = server.arg("action").toInt(); |
|
| 199 | + } |
|
| 200 | + if (server.hasArg("speed")) |
|
| 201 | + { |
|
| 202 | + carSpeed = server.arg("speed").toInt(); |
|
| 203 | + carSpeed = constrain(carSpeed, 0, 255); |
|
| 204 | + } |
|
| 205 | + if (server.hasArg("batt")) |
|
| 206 | + { |
|
| 207 | + batteryS = server.arg("batt").toInt(); |
|
| 208 | + } |
|
| 209 | + applyMotorControl(); |
|
| 210 | + server.send(200, "text/plain", "OK"); |
|
| 211 | +} |
|
| 212 | + |
|
| 213 | +void handleStatus() |
|
| 214 | +{ |
|
| 215 | + String status = "Action: " + String(carAction) + ", Speed: " + String(carSpeed) + ", Batt: " + String(batteryS) + "S"; |
|
| 216 | + server.send(200, "text/plain", status); |
|
| 217 | +} |
|
| 218 | + |
|
| 219 | +void setup() |
|
| 220 | +{ |
|
| 221 | + // Initialize pins |
|
| 222 | + pinMode(M1_IN1, OUTPUT); |
|
| 223 | + pinMode(M1_IN2, OUTPUT); |
|
| 224 | + pinMode(M2_IN1, OUTPUT); |
|
| 225 | + pinMode(M2_IN2, OUTPUT); |
|
| 226 | + |
|
| 227 | + // Initialize motors to off |
|
| 228 | + digitalWrite(M1_IN1, LOW); |
|
| 229 | + digitalWrite(M1_IN2, LOW); |
|
| 230 | + digitalWrite(M2_IN1, LOW); |
|
| 231 | + digitalWrite(M2_IN2, LOW); |
|
| 232 | + |
|
| 233 | + // Set PWM frequency for DRV8871 (supports up to 200kHz, but 20kHz is standard for silence and efficiency) |
|
| 234 | + analogWriteFreq(20000); |
|
| 235 | + |
|
| 236 | + Serial.begin(115200); |
|
| 237 | + delay(100); |
|
| 238 | + |
|
| 239 | + Serial.println("Test..."); |
|
| 240 | + delay(1000); |
|
| 241 | + Serial.println("Test..."); |
|
| 242 | + delay(1000); |
|
| 243 | + Serial.println("Test..."); |
|
| 244 | + delay(1000); |
|
| 245 | + // Configure AP with fixed IP |
|
| 246 | + WiFi.softAPConfig(apIP, apIP, netMsk); |
|
| 247 | + WiFi.softAP(ssid, password); |
|
| 248 | + |
|
| 249 | + IPAddress myIP = WiFi.softAPIP(); |
|
| 250 | + Serial.print("AP IP address: "); |
|
| 251 | + Serial.println(myIP); |
|
| 252 | + |
|
| 253 | + // Configure server routes |
|
| 254 | + server.on("/", handleRoot); |
|
| 255 | + server.on("/set", handleSet); |
|
| 256 | + server.on("/status", handleStatus); |
|
| 257 | + server.begin(); |
|
| 258 | + Serial.println("HTTP server started"); |
|
| 259 | + |
|
| 260 | + // Ensure PWM range 0-255 |
|
| 261 | + analogWriteRange(255); |
|
| 262 | + |
|
| 263 | + // Apply initial motor state |
|
| 264 | + applyMotorControl(); |
|
| 265 | +} |
|
| 266 | + |
|
| 267 | +void loop() |
|
| 268 | +{ |
|
| 269 | + server.handleClient(); |
|
| 270 | + |
|
| 271 | + // Safety Watchdog: Stop motors if no command received for > 1 second |
|
| 272 | + if (carAction != 0 && (millis() - lastCommandTime > SAFETY_TIMEOUT)) { |
|
| 273 | + carAction = 0; |
|
| 274 | + applyMotorControl(); |
|
| 275 | + Serial.println("Safety Timeout: Motors stopped"); |
|
| 276 | + } |
|
| 277 | + |
|
| 278 | + delay(10); |
|
| 279 | +} |
|
| 280 | + |
|
| 281 | + |
|
| 282 | + |
|
| 283 | + |
code-dat/esp866-code-dat/ESP8266-WIFI-motor-1/features.md
| ... | ... | @@ -0,0 +1,32 @@ |
| 1 | +# ESP8266 WiFi Motor Chassis Features |
|
| 2 | + |
|
| 3 | +## 1. Directional Control (9-Way) |
|
| 4 | +- **8 Directions**: Full support for Forward, Backward, Left, Right, and all four diagonal directions (Forward-Left, Forward-Right, Backward-Left, Backward-Right). |
|
| 5 | +- **Dedicated Stop**: Immediate halt on button release and a centralized "OFF" emergency stop button. |
|
| 6 | + |
|
| 7 | +## 2. Advanced Motor Driving |
|
| 8 | +- **Differential Steering**: Implements $Base \pm Offset$ logic for smooth, proportional turning during diagonal movement and pivot rotation. |
|
| 9 | +- **Stall Protection**: Enforces a `minSpeed` floor (100 PWM) to ensure motors maintain torque at low speeds. |
|
| 10 | +- **PWM Optimization**: Optimized for high-frequency (20kHz) PWM modulation for smooth speed control. |
|
| 11 | + |
|
| 12 | +## 3. Safety & Reliability |
|
| 13 | +- **Software Watchdog**: A hardware-safe timeout system stops the motors if no command is received within **500ms**, preventing runaway cars in case of WiFi disconnect. |
|
| 14 | +- **Active Heartbeat**: The client browser sends a "keep-alive" signal every **200ms** while any button is held, ensuring seamless motion through minor signal jitters. |
|
| 15 | +- **Interface Sanity**: Prevents "ghost" presses with event `preventDefault()` and stops motors automatically if the browser window loses focus (`blur` event). |
|
| 16 | + |
|
| 17 | +## 4. Power Management |
|
| 18 | +- **Battery Compensation**: Selectable modes for **2S (7.4V)** and **3S (11.1V)** battery packs. |
|
| 19 | +- **Voltage Scaling**: Automatically reduces PWM duty cycle in 3S mode (scaling factor $\approx 0.66$) to match 2S speed levels and protect motors from over-voltage. |
|
| 20 | + |
|
| 21 | +## 5. Web Interface & Connectivity |
|
| 22 | +- **Standalone Hotspot**: Creates its own WiFi Access Point (`MotorAP`) for direct control without an external router. |
|
| 23 | +- **Responsive Web UI**: A mobile-first controller interface with large touch-friendly buttons and a real-time speed slider. |
|
| 24 | +- **Low Latency**: Lightweight HTTP GET handlers for near-instant control responses. |
|
| 25 | + |
|
| 26 | +## 6. Technical Specifications (Default) |
|
| 27 | +- **Controller**: ESP8266 |
|
| 28 | +- **Motor Pins**: |
|
| 29 | + - Left (M1): Pins 4, 5 |
|
| 30 | + - Right (M2): Pins 0, 2 |
|
| 31 | +- **Default Speed**: 200/255 |
|
| 32 | +- **Safety Window**: 500ms |
code-dat/esp866-code-dat/esp8266-elrs-drv8871-2-1/esp8266-elrs-drv8871-2-1.ino
| ... | ... | @@ -0,0 +1,138 @@ |
| 1 | + |
|
| 2 | +#include <SoftwareSerial.h> |
|
| 3 | + |
|
| 4 | +/* |
|
| 5 | + * ELRS Motor Control Demo for ESP8266 |
|
| 6 | + * |
|
| 7 | + * Hardware Setup: |
|
| 8 | + * - ELRS Receiver: RX -> IO13, TX -> IO12 (SoftwareSerial) |
|
| 9 | + * - Motor 1 (Left): IN1 -> IO4, IN2 -> IO5 |
|
| 10 | + * - Motor 2 (Right): IN1 -> IO0, IN2 -> IO2 |
|
| 11 | + * - DRV8871 pins are externally pulled-up, so active LOW for driving. |
|
| 12 | + */ |
|
| 13 | + |
|
| 14 | +// Motor 1 (Left) |
|
| 15 | +const int M1_IN1 = 4; |
|
| 16 | +const int M1_IN2 = 5; |
|
| 17 | +// Motor 2 (Right) |
|
| 18 | +const int M2_IN1 = 0; |
|
| 19 | +const int M2_IN2 = 2; |
|
| 20 | + |
|
| 21 | +// CRSF Protocol Definitions |
|
| 22 | +#define CRSF_ADDR_MODULE 0xEE |
|
| 23 | +#define CRSF_FRAMETYPE_RC_CHANNELS_PACKED 0x16 |
|
| 24 | + |
|
| 25 | +SoftwareSerial elrsSerial(13, 12); // RX, TX |
|
| 26 | + |
|
| 27 | +void setup() { |
|
| 28 | + Serial.begin(115200); |
|
| 29 | + elrsSerial.begin(420000); // CRSF standard baud rate is 420k |
|
| 30 | + |
|
| 31 | + pinMode(M1_IN1, OUTPUT); |
|
| 32 | + pinMode(M1_IN2, OUTPUT); |
|
| 33 | + pinMode(M2_IN1, OUTPUT); |
|
| 34 | + pinMode(M2_IN2, OUTPUT); |
|
| 35 | + |
|
| 36 | + // Initialize motors to STOP (HIGH because of external pull-up) |
|
| 37 | + digitalWrite(M1_IN1, HIGH); |
|
| 38 | + digitalWrite(M1_IN2, HIGH); |
|
| 39 | + digitalWrite(M2_IN1, HIGH); |
|
| 40 | + digitalWrite(M2_IN2, HIGH); |
|
| 41 | + |
|
| 42 | + Serial.println("ELRS DRV8871 Motor Control Initialized."); |
|
| 43 | +} |
|
| 44 | + |
|
| 45 | +void stopMotors() { |
|
| 46 | + digitalWrite(M1_IN1, HIGH); |
|
| 47 | + digitalWrite(M1_IN2, HIGH); |
|
| 48 | + digitalWrite(M2_IN1, HIGH); |
|
| 49 | + digitalWrite(M2_IN2, HIGH); |
|
| 50 | +} |
|
| 51 | + |
|
| 52 | +void driveMotor(int in1, int in2, int speed) { |
|
| 53 | + // speed: -100 to 100 |
|
| 54 | + if (speed > 10) { |
|
| 55 | + // Forward (using PWM for speed control, since pins are pulled up, we pull LOW) |
|
| 56 | + analogWrite(in1, 255 - map(speed, 0, 100, 0, 255)); |
|
| 57 | + digitalWrite(in2, HIGH); |
|
| 58 | + } else if (speed < -10) { |
|
| 59 | + // Reverse |
|
| 60 | + digitalWrite(in1, HIGH); |
|
| 61 | + analogWrite(in2, 255 - map(abs(speed), 0, 100, 0, 255)); |
|
| 62 | + } else { |
|
| 63 | + // Stop |
|
| 64 | + digitalWrite(in1, HIGH); |
|
| 65 | + digitalWrite(in2, HIGH); |
|
| 66 | + } |
|
| 67 | +} |
|
| 68 | + |
|
| 69 | +void loop() { |
|
| 70 | + // Debug serial input from ELRS even if not a full packet |
|
| 71 | + static unsigned long lastRawDebug = 0; |
|
| 72 | + if (elrsSerial.available() && millis() - lastRawDebug > 1000) { |
|
| 73 | + lastRawDebug = millis(); |
|
| 74 | + Serial.print("ELRS Serial Bytes Available: "); |
|
| 75 | + Serial.println(elrsSerial.available()); |
|
| 76 | + } |
|
| 77 | + |
|
| 78 | + if (elrsSerial.available()) { |
|
| 79 | + uint8_t header = elrsSerial.read(); |
|
| 80 | + if (header == CRSF_ADDR_MODULE) { |
|
| 81 | + uint8_t len = elrsSerial.read(); |
|
| 82 | + uint8_t type = elrsSerial.read(); |
|
| 83 | + |
|
| 84 | + if (type == CRSF_FRAMETYPE_RC_CHANNELS_PACKED) { |
|
| 85 | + uint8_t buffer[22]; |
|
| 86 | + elrsSerial.readBytes(buffer, 22); |
|
| 87 | + |
|
| 88 | + // Debug Raw Packet |
|
| 89 | + static unsigned long lastHexDebug = 0; |
|
| 90 | + if (millis() - lastHexDebug > 500) { |
|
| 91 | + lastHexDebug = millis(); |
|
| 92 | + Serial.print("Raw Hex: "); |
|
| 93 | + Serial.print(header, HEX); Serial.print(" "); |
|
| 94 | + Serial.print(len, HEX); Serial.print(" "); |
|
| 95 | + Serial.print(type, HEX); Serial.print(" "); |
|
| 96 | + for(int i=0; i<22; i++) { |
|
| 97 | + if(buffer[i] < 0x10) Serial.print("0"); |
|
| 98 | + Serial.print(buffer[i], HEX); Serial.print(" "); |
|
| 99 | + } |
|
| 100 | + Serial.println(); |
|
| 101 | + } |
|
| 102 | + |
|
| 103 | + // Simple CRSF channel decoding (11 bits per channel) |
|
| 104 | + // Channel 1: Roll, Channel 2: Pitch, Channel 3: Throttle, Channel 4: Yaw |
|
| 105 | + int ch1 = (buffer[0] | buffer[1] << 8) & 0x07FF; |
|
| 106 | + int ch2 = (buffer[1] >> 3 | buffer[2] << 5) & 0x07FF; |
|
| 107 | + int ch3 = (buffer[2] >> 6 | buffer[3] << 2 | buffer[4] << 10) & 0x07FF; |
|
| 108 | + int ch4 = (buffer[4] >> 1 | buffer[5] << 7) & 0x07FF; |
|
| 109 | + |
|
| 110 | + // Map ELRS channels (typically 172-1811, center 992) to -100 to 100 |
|
| 111 | + int throttle = map(ch3, 172, 1811, -100, 100); |
|
| 112 | + int steering = map(ch1, 172, 1811, -100, 100); |
|
| 113 | + |
|
| 114 | + // Simple Differential Drive logic |
|
| 115 | + int leftSpeed = throttle + steering; |
|
| 116 | + int rightSpeed = throttle - steering; |
|
| 117 | + |
|
| 118 | + leftSpeed = constrain(leftSpeed, -100, 100); |
|
| 119 | + rightSpeed = constrain(rightSpeed, -100, 100); |
|
| 120 | + |
|
| 121 | + // Debugging output |
|
| 122 | + static unsigned long lastDebug = 0; |
|
| 123 | + if (millis() - lastDebug > 100) { |
|
| 124 | + lastDebug = millis(); |
|
| 125 | + Serial.print("CH1: "); Serial.print(ch1); |
|
| 126 | + Serial.print(" | CH3: "); Serial.print(ch3); |
|
| 127 | + Serial.print(" | Thr: "); Serial.print(throttle); |
|
| 128 | + Serial.print(" | Str: "); Serial.print(steering); |
|
| 129 | + Serial.print(" | L: "); Serial.print(leftSpeed); |
|
| 130 | + Serial.print(" | R: "); Serial.println(rightSpeed); |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + driveMotor(M1_IN1, M1_IN2, leftSpeed); |
|
| 134 | + driveMotor(M2_IN1, M2_IN2, rightSpeed); |
|
| 135 | + } |
|
| 136 | + } |
|
| 137 | + } |
|
| 138 | +} |
code-dat/esp866-code-dat/esp866-code-dat.md
| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | +
|
|
| 2 | +
|
|
| 3 | +# esp866-code-dat
|
|
| 4 | +
|
|
| 5 | +- [[esp866-code-dat]] - [[ESP8266-SDK-dat]] - [[ESP-dat]]
|
|
| 6 | +
|
|
| 7 | +- [[CRSF-dat]]
|
|
| 8 | +
|
|
| 9 | +## design
|
|
| 10 | +
|
|
| 11 | +slow software serial - can not be more than 115200 baud, otherwise the data will be corrupted.
|
|
| 12 | +
|
|
| 13 | +The data you shared is raw CRSF binary packets, but they look slightly corrupted or "jittery," which is very common when using SoftwareSerial at 115200 on an ESP8266.
|
|
| 14 | +
|
|
| 15 | +- [[CRSF-dat]]
|
|
| 16 | +
|
|
| 17 | + 0B 56 FE D8 10 0B 56 FE D8 10 0B 52 FE 84 D8 10
|
|
| 18 | + 0B 52 FE D8 10 0B 56 FE D8 10 0B 52 FF D8 10 0B
|
|
| 19 | + 52 FF D9 10 99 FF 44 D8 10 0B 56 FE D8 10 0B 52
|
|
| 20 | + FF D8 10 0B 56 FE D8 10 0B 56 FF 25 26 12 4B A2
|
|
| 21 | + D8 10 0B 52 FE D8 10 0B 56 FE A6 12 4B D8 10 99
|
|
| 22 | + FF D8 10 0B 56 FE E4 26 12 4B A2 A6 12 4B D8 10
|
|
| 23 | + 0B 52 FE D8 10 0B 56 FE D8 10 0B 56 FF 84 D8 10
|
|
| 24 | + 99 FF D8 10 0B 52 FF D8 10 0B 56 FE A6 12 4B A4
|
|
| 25 | + D8 10 0B 56 FE D8 10 FF D8 10 0B 52 FE D8 10 0B
|
|
| 26 | + 56 FE D8 10 0B 56 FE 44 12 4B FF D8 10 0B 52 FF
|
|
| 27 | + D8 10 0B 56 FF D9 10 FE D8 10 0B 56 FF 80 D8 10
|
|
| 28 | + 0B 56 FF D8 10 0B 56 FF 12 4B 54 FF 84 D8 10 0B
|
|
| 29 | + 52 FF D8 10 0B 52 FF D8 10 0B 52 FF D8 10 0B 56
|
|
| 30 | + FF D9 10 FE C4 D9 10 0B 56 FF D8 10 FE 12 4B 02
|
|
| 31 | + 4E D8 10 0B D2 FF D8 10 FE A2 D8 10 0B 52 FF D8
|
|
| 32 | + 10 0B 56 FF D8 10 FE D8 10 0B 56 FF 00 80 D8 10
|
|
| 33 | + 0B 56 FE D8 10 0B 56 FE D8 10 0B 52 FE D8 10 FF
|
|
| 34 | + D8 10 0B 56 FE 80 D8 10 0B 52 FE D8 10 0B 56 FE
|
|
| 35 | + D8 10 0B 52 FE D8 10 0B 52 FE D8 10 99 FF 40 D8
|
|
| 36 | +
|
|
| 37 | +
|
|
| 38 | +## ref
|
|
| 39 | +
|
|
| 40 | +
|
code-dat/esp866-code-dat/printAllChannels/printAllChannels.ino
| ... | ... | @@ -0,0 +1,37 @@ |
| 1 | +#include <AlfredoCRSF.h> |
|
| 2 | +#include <SoftwareSerial.h> |
|
| 3 | + |
|
| 4 | +#define PIN_RX 13 |
|
| 5 | +#define PIN_TX 12 |
|
| 6 | + |
|
| 7 | +// Set up a new SoftwareSerial object |
|
| 8 | +SoftwareSerial crsfSerial(PIN_RX, PIN_TX); |
|
| 9 | +AlfredoCRSF crsf; |
|
| 10 | + |
|
| 11 | +void setup() |
|
| 12 | +{ |
|
| 13 | + Serial.begin(115200); |
|
| 14 | + Serial.println("COM Serial initialized"); |
|
| 15 | + |
|
| 16 | + crsfSerial.begin(115200); |
|
| 17 | + |
|
| 18 | + crsf.begin(crsfSerial); |
|
| 19 | +} |
|
| 20 | + |
|
| 21 | +void loop() |
|
| 22 | +{ |
|
| 23 | + // Must call crsf.update() in loop() to process data |
|
| 24 | + crsf.update(); |
|
| 25 | + printChannels(); |
|
| 26 | +} |
|
| 27 | + |
|
| 28 | +//Use crsf.getChannel(x) to get us channel values (1-16). |
|
| 29 | +void printChannels() |
|
| 30 | +{ |
|
| 31 | + for (int ChannelNum = 1; ChannelNum <= 16; ChannelNum++) |
|
| 32 | + { |
|
| 33 | + Serial.print(crsf.getChannel(ChannelNum)); |
|
| 34 | + Serial.print(", "); |
|
| 35 | + } |
|
| 36 | + Serial.println(" "); |
|
| 37 | +} |
|
| ... | ... | \ No newline at end of file |
code-dat/esp866-code-dat/soft-serial-debug-2/soft-serial-debug-2.ino
| ... | ... | @@ -0,0 +1,37 @@ |
| 1 | +#include <SoftwareSerial.h> |
|
| 2 | +SoftwareSerial s(13, 12); |
|
| 3 | +uint8_t buffer[64]; |
|
| 4 | +int p = 0; |
|
| 5 | +unsigned long t = 0; |
|
| 6 | +void setup() { |
|
| 7 | + pinMode(13, INPUT_PULLUP); // Enable internal pull-up on RX pin |
|
| 8 | + Serial.begin(115200); |
|
| 9 | + s.begin(115200); |
|
| 10 | + Serial.println("\n--- CRSF V5 Filtered ---"); |
|
| 11 | +} |
|
| 12 | +void loop() { |
|
| 13 | + while (s.available()) { |
|
| 14 | + uint8_t v = s.read(); |
|
| 15 | + if (p == 0) { |
|
| 16 | + if (v == 0xC4 || v == 0xC8 || v == 0xEE) p = 1; |
|
| 17 | + } else { |
|
| 18 | + buffer[p++] = v; |
|
| 19 | + if (p >= 26) { |
|
| 20 | + unsigned int rc[4]; |
|
| 21 | + rc[0] = (buffer[3] | buffer[4] << 8) & 0x7FF; |
|
| 22 | + rc[1] = (buffer[4] >> 3 | buffer[5] << 5) & 0x7FF; |
|
| 23 | + rc[2] = (buffer[5] >> 6 | buffer[6] << 2 | buffer[7] << 10) & 0x7FF; |
|
| 24 | + rc[3] = (buffer[7] >> 1 | buffer[8] << 7) & 0x7FF; |
|
| 25 | + bool ok = true; |
|
| 26 | + for(int i=0; i<4; i++) if(rc[i] < 170 || rc[i] > 1820) ok = false; |
|
| 27 | + if (ok) { // Removed the 100ms delay for faster output |
|
| 28 | + Serial.print("CH1:"); Serial.print(rc[0]); |
|
| 29 | + Serial.print(" CH2:"); Serial.print(rc[1]); |
|
| 30 | + Serial.print(" CH3:"); Serial.print(rc[2]); |
|
| 31 | + Serial.print(" CH4:"); Serial.println(rc[3]); |
|
| 32 | + } |
|
| 33 | + p = 0; |
|
| 34 | + } |
|
| 35 | + } |
|
| 36 | + } |
|
| 37 | +} |
code-dat/esp866-code-dat/soft-serial-debug/soft-serial-debug.ino
| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | +#include <SoftwareSerial.h>
|
|
| 2 | +
|
|
| 3 | +SoftwareSerial soft(13, 12); // RX, TX
|
|
| 4 | +
|
|
| 5 | +void setup() {
|
|
| 6 | + Serial.begin(115200);
|
|
| 7 | + soft.begin(115200);
|
|
| 8 | + Serial.println("\n--- CRSF Raw Hex Debugger ---");
|
|
| 9 | +}
|
|
| 10 | +
|
|
| 11 | +void loop() {
|
|
| 12 | + if (soft.available()) {
|
|
| 13 | + uint8_t incomingByte = soft.read();
|
|
| 14 | +
|
|
| 15 | + // Print as Hex
|
|
| 16 | + if (incomingByte < 0x10) Serial.print("0");
|
|
| 17 | + Serial.print(incomingByte, HEX);
|
|
| 18 | + Serial.print(" ");
|
|
| 19 | +
|
|
| 20 | + // Newline every 16 bytes
|
|
| 21 | + static int byteCount = 0;
|
|
| 22 | + byteCount++;
|
|
| 23 | + if (byteCount >= 16) {
|
|
| 24 | + Serial.println();
|
|
| 25 | + byteCount = 0;
|
|
| 26 | + }
|
|
| 27 | + }
|
|
| 28 | +} |
|
| ... | ... | \ No newline at end of file |
code-dat/esp866-dat/ESP8266-WIFI-motor-1/ESP8266-WIFI-motor-1.ino
| ... | ... | @@ -1,283 +0,0 @@ |
| 1 | -#include <ESP8266WiFi.h> |
|
| 2 | -#include <ESP8266WebServer.h> |
|
| 3 | - |
|
| 4 | -// WiFi AP settings (fixed IP) |
|
| 5 | -const char *ssid = "MotorAP"; |
|
| 6 | -const char *password = "motorpass"; // set to "" for open AP |
|
| 7 | -IPAddress apIP(192, 168, 4, 1); |
|
| 8 | -IPAddress netMsk(255, 255, 255, 0); |
|
| 9 | - |
|
| 10 | -// Define pins for motor control |
|
| 11 | -// Motor 1 (Left) |
|
| 12 | -const int M1_IN1 = 4; |
|
| 13 | -const int M1_IN2 = 5; |
|
| 14 | -// Motor 2 (Right) |
|
| 15 | -const int M2_IN1 = 0; |
|
| 16 | -const int M2_IN2 = 2; |
|
| 17 | - |
|
| 18 | -int carSpeed = 200; // 0..255 (Base Speed) |
|
| 19 | -int minSpeed = 100; // Minimum speed to avoid stalling |
|
| 20 | -int turnDiff = 85; // Increased for more "agility" (sharper turns) |
|
| 21 | -int carAction = 0; // 0: stop, 1: forward, 2: backward, 3: left, 4: right |
|
| 22 | -unsigned long lastCommandTime = 0; |
|
| 23 | -const unsigned long SAFETY_TIMEOUT = 300; // Even tighter for high responsiveness |
|
| 24 | - |
|
| 25 | -int batteryS = 3; // Default 3S (11.1V) |
|
| 26 | - |
|
| 27 | -ESP8266WebServer server(80); |
|
| 28 | - |
|
| 29 | -int getCompensatedSpeed(int baseSpeed) { |
|
| 30 | - if (baseSpeed == 0) return 0; |
|
| 31 | - // Simple inverse scaling: |
|
| 32 | - // If it's 3S (11.1V), we scale DOWN the speed compared to 2S (7.4V) |
|
| 33 | - // Scale factor = 7.4 / 11.1 = ~0.66 |
|
| 34 | - if (batteryS == 3) { |
|
| 35 | - return (int)(baseSpeed * 0.66); |
|
| 36 | - } |
|
| 37 | - return baseSpeed; // Use raw speed for 2S |
|
| 38 | -} |
|
| 39 | - |
|
| 40 | -void applyMotorControl() |
|
| 41 | -{ |
|
| 42 | - int leftSpeed = 0; |
|
| 43 | - int rightSpeed = 0; |
|
| 44 | - |
|
| 45 | - int compensatedSpeed = getCompensatedSpeed(carSpeed); |
|
| 46 | - int compensatedTurnDiff = getCompensatedSpeed(turnDiff); |
|
| 47 | - |
|
| 48 | - // Helper to ensure motor gets enough power or is OFF |
|
| 49 | - auto normalizeSpeed = [&](int s) { |
|
| 50 | - if (s <= 0) return 0; |
|
| 51 | - if (s < minSpeed) return minSpeed; |
|
| 52 | - if (s > 255) return 255; |
|
| 53 | - return s; |
|
| 54 | - }; |
|
| 55 | - |
|
| 56 | - switch (carAction) |
|
| 57 | - { |
|
| 58 | - case 0: // Stop |
|
| 59 | - leftSpeed = rightSpeed = 0; |
|
| 60 | - break; |
|
| 61 | - case 1: // Forward |
|
| 62 | - leftSpeed = rightSpeed = compensatedSpeed; |
|
| 63 | - break; |
|
| 64 | - case 2: // Backward |
|
| 65 | - leftSpeed = rightSpeed = -compensatedSpeed; |
|
| 66 | - break; |
|
| 67 | - case 3: // Spin Left |
|
| 68 | - leftSpeed = -compensatedSpeed; |
|
| 69 | - rightSpeed = compensatedSpeed; |
|
| 70 | - break; |
|
| 71 | - case 4: // Spin Right |
|
| 72 | - leftSpeed = compensatedSpeed; |
|
| 73 | - rightSpeed = -compensatedSpeed; |
|
| 74 | - break; |
|
| 75 | - case 5: // Forward Left (Curve) - Swapped |
|
| 76 | - leftSpeed = compensatedSpeed + compensatedTurnDiff; |
|
| 77 | - rightSpeed = compensatedSpeed - compensatedTurnDiff; |
|
| 78 | - break; |
|
| 79 | - case 6: // Forward Right (Curve) - Swapped |
|
| 80 | - leftSpeed = compensatedSpeed - compensatedTurnDiff; |
|
| 81 | - rightSpeed = compensatedSpeed + compensatedTurnDiff; |
|
| 82 | - break; |
|
| 83 | - case 7: // Backward Left (Curve) |
|
| 84 | - leftSpeed = -(compensatedSpeed + compensatedTurnDiff); |
|
| 85 | - rightSpeed = -(compensatedSpeed - compensatedTurnDiff); |
|
| 86 | - break; |
|
| 87 | - case 8: // Backward Right (Curve) |
|
| 88 | - leftSpeed = -(compensatedSpeed - compensatedTurnDiff); |
|
| 89 | - rightSpeed = -(compensatedSpeed + compensatedTurnDiff); |
|
| 90 | - break; |
|
| 91 | - default: |
|
| 92 | - leftSpeed = rightSpeed = 0; |
|
| 93 | - break; |
|
| 94 | - } |
|
| 95 | - |
|
| 96 | - // Apply Left Motor |
|
| 97 | - int absL = abs(leftSpeed); |
|
| 98 | - int finalL = normalizeSpeed(absL); |
|
| 99 | - if (leftSpeed > 0) { |
|
| 100 | - analogWrite(M1_IN1, finalL); |
|
| 101 | - digitalWrite(M1_IN2, LOW); |
|
| 102 | - } else if (leftSpeed < 0) { |
|
| 103 | - digitalWrite(M1_IN1, LOW); |
|
| 104 | - analogWrite(M1_IN2, finalL); |
|
| 105 | - } else { |
|
| 106 | - digitalWrite(M1_IN1, LOW); |
|
| 107 | - digitalWrite(M1_IN2, LOW); |
|
| 108 | - } |
|
| 109 | - |
|
| 110 | - // Apply Right Motor |
|
| 111 | - int absR = abs(rightSpeed); |
|
| 112 | - int finalR = normalizeSpeed(absR); |
|
| 113 | - if (rightSpeed > 0) { |
|
| 114 | - analogWrite(M2_IN1, finalR); |
|
| 115 | - digitalWrite(M2_IN2, LOW); |
|
| 116 | - } else if (rightSpeed < 0) { |
|
| 117 | - digitalWrite(M2_IN1, LOW); |
|
| 118 | - analogWrite(M2_IN2, finalR); |
|
| 119 | - } else { |
|
| 120 | - digitalWrite(M2_IN1, LOW); |
|
| 121 | - digitalWrite(M2_IN2, LOW); |
|
| 122 | - } |
|
| 123 | -} |
|
| 124 | - |
|
| 125 | -String pageRoot() |
|
| 126 | -{ |
|
| 127 | - String html = "<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no\">"; |
|
| 128 | - html += "<style>"; |
|
| 129 | - html += "body { font-family: sans-serif; text-align: center; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #f0f0f0; }"; |
|
| 130 | - html += ".btn { width: 80px; height: 80px; margin: 10px; border-radius: 15px; border: none; background: #3498db; color: white; font-size: 24px; font-weight: bold; cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent; box-shadow: 0 4px #2980b9; }"; |
|
| 131 | - html += ".btn:active { background: #2980b9; box-shadow: 0 2px #1c5982; transform: translateY(2px); }"; |
|
| 132 | - html += ".stop-btn { background: #e74c3c; box-shadow: 0 4px #c0392b; }"; |
|
| 133 | - html += ".stop-btn:active { background: #c0392b; box-shadow: 0 2px #962d22; }"; |
|
| 134 | - html += ".row { display: flex; justify-content: center; }"; |
|
| 135 | - html += ".speed-container { margin-top: 20px; width: 80%; }"; |
|
| 136 | - html += "input[type=range] { width: 100%; height: 25px; cursor: pointer; }"; |
|
| 137 | - html += "select { padding: 8px; font-size: 16px; border-radius: 5px; }"; |
|
| 138 | - html += "</style></head><body>"; |
|
| 139 | - html += "<h2>Motor Control</h2>"; |
|
| 140 | - |
|
| 141 | - auto btnHtml = [&](int act, String icon) { |
|
| 142 | - String s_act = String(act); |
|
| 143 | - return " <button class='btn' onmousedown=\"setAction(" + s_act + ", event)\" ontouchstart=\"setAction(" + s_act + ", event)\" " |
|
| 144 | - "onmouseup=\"setAction(0, event)\" onmouseleave=\"setAction(0, event)\" ontouchend=\"setAction(0, event)\" ontouchcancel=\"setAction(0, event)\">" + icon + "</button>"; |
|
| 145 | - }; |
|
| 146 | - |
|
| 147 | - html += "<div class='row'>"; |
|
| 148 | - html += btnHtml(5, "↖"); |
|
| 149 | - html += btnHtml(1, "↑"); |
|
| 150 | - html += btnHtml(6, "↗"); |
|
| 151 | - html += "</div>"; |
|
| 152 | - |
|
| 153 | - html += "<div class='row'>"; |
|
| 154 | - html += btnHtml(3, "←"); |
|
| 155 | - html += " <button class='btn stop-btn' onclick=\"setAction(0, event)\" ontouchstart=\"setAction(0, event)\">OFF</button>"; |
|
| 156 | - html += btnHtml(4, "→"); |
|
| 157 | - html += "</div>"; |
|
| 158 | - |
|
| 159 | - html += "<div class='row'>"; |
|
| 160 | - html += btnHtml(7, "↙"); |
|
| 161 | - html += btnHtml(2, "↓"); |
|
| 162 | - html += btnHtml(8, "↘"); |
|
| 163 | - html += "</div>"; |
|
| 164 | - |
|
| 165 | - html += "<div class='speed-container'>"; |
|
| 166 | - html += " Speed: <span id='v'>" + String(carSpeed) + "</span>"; |
|
| 167 | - html += " <input type='range' id='s' min='100' max='255' value='" + String(carSpeed) + "' oninput='setSpeed(this.value)' />"; |
|
| 168 | - html += "</div>"; |
|
| 169 | - |
|
| 170 | - html += "<div class='speed-container'>"; |
|
| 171 | - html += " Battery Mode: <select onchange=\"setBattery(this.value)\">"; |
|
| 172 | - html += " <option value='2'" + String(batteryS == 2 ? " selected" : "") + ">2S (7.4V)</option>"; |
|
| 173 | - html += " <option value='3'" + String(batteryS == 3 ? " selected" : "") + ">3S (11.1V)</option>"; |
|
| 174 | - html += " </select>"; |
|
| 175 | - html += "</div>"; |
|
| 176 | - |
|
| 177 | - html += "<script>"; |
|
| 178 | - html += "let lastA = -1; let lastS = -1;"; |
|
| 179 | - html += "function setAction(a, e, f) { if(e) { e.preventDefault(); } if(!f && a == lastA && a != 0) return; lastA = a; fetch('/set?action=' + a).catch(e => console.error(e)); }"; |
|
| 180 | - html += "function setSpeed(s) { if(s == lastS) return; lastS = s; document.getElementById('v').innerText = s; fetch('/set?speed=' + s).catch(e => console.error(e)); }"; |
|
| 181 | - html += "function setBattery(b) { fetch('/set?batt=' + b); }"; |
|
| 182 | - html += "setInterval(() => { if(lastA > 0) setAction(lastA, null, true); }, 100);"; // "Pro" agility: 100ms heartbeat |
|
| 183 | - html += "</script></body></html>"; |
|
| 184 | - return html; |
|
| 185 | -} |
|
| 186 | - |
|
| 187 | -void handleRoot() |
|
| 188 | -{ |
|
| 189 | - server.send(200, "text/html", pageRoot()); |
|
| 190 | -} |
|
| 191 | - |
|
| 192 | -void handleSet() |
|
| 193 | -{ |
|
| 194 | - server.client().setNoDelay(true); // Disable Nagle's algorithm for faster response |
|
| 195 | - lastCommandTime = millis(); // Refresh safety watchdog |
|
| 196 | - if (server.hasArg("action")) |
|
| 197 | - { |
|
| 198 | - carAction = server.arg("action").toInt(); |
|
| 199 | - } |
|
| 200 | - if (server.hasArg("speed")) |
|
| 201 | - { |
|
| 202 | - carSpeed = server.arg("speed").toInt(); |
|
| 203 | - carSpeed = constrain(carSpeed, 0, 255); |
|
| 204 | - } |
|
| 205 | - if (server.hasArg("batt")) |
|
| 206 | - { |
|
| 207 | - batteryS = server.arg("batt").toInt(); |
|
| 208 | - } |
|
| 209 | - applyMotorControl(); |
|
| 210 | - server.send(200, "text/plain", "OK"); |
|
| 211 | -} |
|
| 212 | - |
|
| 213 | -void handleStatus() |
|
| 214 | -{ |
|
| 215 | - String status = "Action: " + String(carAction) + ", Speed: " + String(carSpeed) + ", Batt: " + String(batteryS) + "S"; |
|
| 216 | - server.send(200, "text/plain", status); |
|
| 217 | -} |
|
| 218 | - |
|
| 219 | -void setup() |
|
| 220 | -{ |
|
| 221 | - // Initialize pins |
|
| 222 | - pinMode(M1_IN1, OUTPUT); |
|
| 223 | - pinMode(M1_IN2, OUTPUT); |
|
| 224 | - pinMode(M2_IN1, OUTPUT); |
|
| 225 | - pinMode(M2_IN2, OUTPUT); |
|
| 226 | - |
|
| 227 | - // Initialize motors to off |
|
| 228 | - digitalWrite(M1_IN1, LOW); |
|
| 229 | - digitalWrite(M1_IN2, LOW); |
|
| 230 | - digitalWrite(M2_IN1, LOW); |
|
| 231 | - digitalWrite(M2_IN2, LOW); |
|
| 232 | - |
|
| 233 | - // Set PWM frequency for DRV8871 (supports up to 200kHz, but 20kHz is standard for silence and efficiency) |
|
| 234 | - analogWriteFreq(20000); |
|
| 235 | - |
|
| 236 | - Serial.begin(115200); |
|
| 237 | - delay(100); |
|
| 238 | - |
|
| 239 | - Serial.println("Test..."); |
|
| 240 | - delay(1000); |
|
| 241 | - Serial.println("Test..."); |
|
| 242 | - delay(1000); |
|
| 243 | - Serial.println("Test..."); |
|
| 244 | - delay(1000); |
|
| 245 | - // Configure AP with fixed IP |
|
| 246 | - WiFi.softAPConfig(apIP, apIP, netMsk); |
|
| 247 | - WiFi.softAP(ssid, password); |
|
| 248 | - |
|
| 249 | - IPAddress myIP = WiFi.softAPIP(); |
|
| 250 | - Serial.print("AP IP address: "); |
|
| 251 | - Serial.println(myIP); |
|
| 252 | - |
|
| 253 | - // Configure server routes |
|
| 254 | - server.on("/", handleRoot); |
|
| 255 | - server.on("/set", handleSet); |
|
| 256 | - server.on("/status", handleStatus); |
|
| 257 | - server.begin(); |
|
| 258 | - Serial.println("HTTP server started"); |
|
| 259 | - |
|
| 260 | - // Ensure PWM range 0-255 |
|
| 261 | - analogWriteRange(255); |
|
| 262 | - |
|
| 263 | - // Apply initial motor state |
|
| 264 | - applyMotorControl(); |
|
| 265 | -} |
|
| 266 | - |
|
| 267 | -void loop() |
|
| 268 | -{ |
|
| 269 | - server.handleClient(); |
|
| 270 | - |
|
| 271 | - // Safety Watchdog: Stop motors if no command received for > 1 second |
|
| 272 | - if (carAction != 0 && (millis() - lastCommandTime > SAFETY_TIMEOUT)) { |
|
| 273 | - carAction = 0; |
|
| 274 | - applyMotorControl(); |
|
| 275 | - Serial.println("Safety Timeout: Motors stopped"); |
|
| 276 | - } |
|
| 277 | - |
|
| 278 | - delay(10); |
|
| 279 | -} |
|
| 280 | - |
|
| 281 | - |
|
| 282 | - |
|
| 283 | - |
code-dat/esp866-dat/ESP8266-WIFI-motor-1/features.md
| ... | ... | @@ -1,32 +0,0 @@ |
| 1 | -# ESP8266 WiFi Motor Chassis Features |
|
| 2 | - |
|
| 3 | -## 1. Directional Control (9-Way) |
|
| 4 | -- **8 Directions**: Full support for Forward, Backward, Left, Right, and all four diagonal directions (Forward-Left, Forward-Right, Backward-Left, Backward-Right). |
|
| 5 | -- **Dedicated Stop**: Immediate halt on button release and a centralized "OFF" emergency stop button. |
|
| 6 | - |
|
| 7 | -## 2. Advanced Motor Driving |
|
| 8 | -- **Differential Steering**: Implements $Base \pm Offset$ logic for smooth, proportional turning during diagonal movement and pivot rotation. |
|
| 9 | -- **Stall Protection**: Enforces a `minSpeed` floor (100 PWM) to ensure motors maintain torque at low speeds. |
|
| 10 | -- **PWM Optimization**: Optimized for high-frequency (20kHz) PWM modulation for smooth speed control. |
|
| 11 | - |
|
| 12 | -## 3. Safety & Reliability |
|
| 13 | -- **Software Watchdog**: A hardware-safe timeout system stops the motors if no command is received within **500ms**, preventing runaway cars in case of WiFi disconnect. |
|
| 14 | -- **Active Heartbeat**: The client browser sends a "keep-alive" signal every **200ms** while any button is held, ensuring seamless motion through minor signal jitters. |
|
| 15 | -- **Interface Sanity**: Prevents "ghost" presses with event `preventDefault()` and stops motors automatically if the browser window loses focus (`blur` event). |
|
| 16 | - |
|
| 17 | -## 4. Power Management |
|
| 18 | -- **Battery Compensation**: Selectable modes for **2S (7.4V)** and **3S (11.1V)** battery packs. |
|
| 19 | -- **Voltage Scaling**: Automatically reduces PWM duty cycle in 3S mode (scaling factor $\approx 0.66$) to match 2S speed levels and protect motors from over-voltage. |
|
| 20 | - |
|
| 21 | -## 5. Web Interface & Connectivity |
|
| 22 | -- **Standalone Hotspot**: Creates its own WiFi Access Point (`MotorAP`) for direct control without an external router. |
|
| 23 | -- **Responsive Web UI**: A mobile-first controller interface with large touch-friendly buttons and a real-time speed slider. |
|
| 24 | -- **Low Latency**: Lightweight HTTP GET handlers for near-instant control responses. |
|
| 25 | - |
|
| 26 | -## 6. Technical Specifications (Default) |
|
| 27 | -- **Controller**: ESP8266 |
|
| 28 | -- **Motor Pins**: |
|
| 29 | - - Left (M1): Pins 4, 5 |
|
| 30 | - - Right (M2): Pins 0, 2 |
|
| 31 | -- **Default Speed**: 200/255 |
|
| 32 | -- **Safety Window**: 500ms |
code-dat/esp866-dat/esp8266-elrs-drv8871-2-1/esp8266-elrs-drv8871-2-1.ino
| ... | ... | @@ -1,138 +0,0 @@ |
| 1 | - |
|
| 2 | -#include <SoftwareSerial.h> |
|
| 3 | - |
|
| 4 | -/* |
|
| 5 | - * ELRS Motor Control Demo for ESP8266 |
|
| 6 | - * |
|
| 7 | - * Hardware Setup: |
|
| 8 | - * - ELRS Receiver: RX -> IO13, TX -> IO12 (SoftwareSerial) |
|
| 9 | - * - Motor 1 (Left): IN1 -> IO4, IN2 -> IO5 |
|
| 10 | - * - Motor 2 (Right): IN1 -> IO0, IN2 -> IO2 |
|
| 11 | - * - DRV8871 pins are externally pulled-up, so active LOW for driving. |
|
| 12 | - */ |
|
| 13 | - |
|
| 14 | -// Motor 1 (Left) |
|
| 15 | -const int M1_IN1 = 4; |
|
| 16 | -const int M1_IN2 = 5; |
|
| 17 | -// Motor 2 (Right) |
|
| 18 | -const int M2_IN1 = 0; |
|
| 19 | -const int M2_IN2 = 2; |
|
| 20 | - |
|
| 21 | -// CRSF Protocol Definitions |
|
| 22 | -#define CRSF_ADDR_MODULE 0xEE |
|
| 23 | -#define CRSF_FRAMETYPE_RC_CHANNELS_PACKED 0x16 |
|
| 24 | - |
|
| 25 | -SoftwareSerial elrsSerial(13, 12); // RX, TX |
|
| 26 | - |
|
| 27 | -void setup() { |
|
| 28 | - Serial.begin(115200); |
|
| 29 | - elrsSerial.begin(420000); // CRSF standard baud rate is 420k |
|
| 30 | - |
|
| 31 | - pinMode(M1_IN1, OUTPUT); |
|
| 32 | - pinMode(M1_IN2, OUTPUT); |
|
| 33 | - pinMode(M2_IN1, OUTPUT); |
|
| 34 | - pinMode(M2_IN2, OUTPUT); |
|
| 35 | - |
|
| 36 | - // Initialize motors to STOP (HIGH because of external pull-up) |
|
| 37 | - digitalWrite(M1_IN1, HIGH); |
|
| 38 | - digitalWrite(M1_IN2, HIGH); |
|
| 39 | - digitalWrite(M2_IN1, HIGH); |
|
| 40 | - digitalWrite(M2_IN2, HIGH); |
|
| 41 | - |
|
| 42 | - Serial.println("ELRS DRV8871 Motor Control Initialized."); |
|
| 43 | -} |
|
| 44 | - |
|
| 45 | -void stopMotors() { |
|
| 46 | - digitalWrite(M1_IN1, HIGH); |
|
| 47 | - digitalWrite(M1_IN2, HIGH); |
|
| 48 | - digitalWrite(M2_IN1, HIGH); |
|
| 49 | - digitalWrite(M2_IN2, HIGH); |
|
| 50 | -} |
|
| 51 | - |
|
| 52 | -void driveMotor(int in1, int in2, int speed) { |
|
| 53 | - // speed: -100 to 100 |
|
| 54 | - if (speed > 10) { |
|
| 55 | - // Forward (using PWM for speed control, since pins are pulled up, we pull LOW) |
|
| 56 | - analogWrite(in1, 255 - map(speed, 0, 100, 0, 255)); |
|
| 57 | - digitalWrite(in2, HIGH); |
|
| 58 | - } else if (speed < -10) { |
|
| 59 | - // Reverse |
|
| 60 | - digitalWrite(in1, HIGH); |
|
| 61 | - analogWrite(in2, 255 - map(abs(speed), 0, 100, 0, 255)); |
|
| 62 | - } else { |
|
| 63 | - // Stop |
|
| 64 | - digitalWrite(in1, HIGH); |
|
| 65 | - digitalWrite(in2, HIGH); |
|
| 66 | - } |
|
| 67 | -} |
|
| 68 | - |
|
| 69 | -void loop() { |
|
| 70 | - // Debug serial input from ELRS even if not a full packet |
|
| 71 | - static unsigned long lastRawDebug = 0; |
|
| 72 | - if (elrsSerial.available() && millis() - lastRawDebug > 1000) { |
|
| 73 | - lastRawDebug = millis(); |
|
| 74 | - Serial.print("ELRS Serial Bytes Available: "); |
|
| 75 | - Serial.println(elrsSerial.available()); |
|
| 76 | - } |
|
| 77 | - |
|
| 78 | - if (elrsSerial.available()) { |
|
| 79 | - uint8_t header = elrsSerial.read(); |
|
| 80 | - if (header == CRSF_ADDR_MODULE) { |
|
| 81 | - uint8_t len = elrsSerial.read(); |
|
| 82 | - uint8_t type = elrsSerial.read(); |
|
| 83 | - |
|
| 84 | - if (type == CRSF_FRAMETYPE_RC_CHANNELS_PACKED) { |
|
| 85 | - uint8_t buffer[22]; |
|
| 86 | - elrsSerial.readBytes(buffer, 22); |
|
| 87 | - |
|
| 88 | - // Debug Raw Packet |
|
| 89 | - static unsigned long lastHexDebug = 0; |
|
| 90 | - if (millis() - lastHexDebug > 500) { |
|
| 91 | - lastHexDebug = millis(); |
|
| 92 | - Serial.print("Raw Hex: "); |
|
| 93 | - Serial.print(header, HEX); Serial.print(" "); |
|
| 94 | - Serial.print(len, HEX); Serial.print(" "); |
|
| 95 | - Serial.print(type, HEX); Serial.print(" "); |
|
| 96 | - for(int i=0; i<22; i++) { |
|
| 97 | - if(buffer[i] < 0x10) Serial.print("0"); |
|
| 98 | - Serial.print(buffer[i], HEX); Serial.print(" "); |
|
| 99 | - } |
|
| 100 | - Serial.println(); |
|
| 101 | - } |
|
| 102 | - |
|
| 103 | - // Simple CRSF channel decoding (11 bits per channel) |
|
| 104 | - // Channel 1: Roll, Channel 2: Pitch, Channel 3: Throttle, Channel 4: Yaw |
|
| 105 | - int ch1 = (buffer[0] | buffer[1] << 8) & 0x07FF; |
|
| 106 | - int ch2 = (buffer[1] >> 3 | buffer[2] << 5) & 0x07FF; |
|
| 107 | - int ch3 = (buffer[2] >> 6 | buffer[3] << 2 | buffer[4] << 10) & 0x07FF; |
|
| 108 | - int ch4 = (buffer[4] >> 1 | buffer[5] << 7) & 0x07FF; |
|
| 109 | - |
|
| 110 | - // Map ELRS channels (typically 172-1811, center 992) to -100 to 100 |
|
| 111 | - int throttle = map(ch3, 172, 1811, -100, 100); |
|
| 112 | - int steering = map(ch1, 172, 1811, -100, 100); |
|
| 113 | - |
|
| 114 | - // Simple Differential Drive logic |
|
| 115 | - int leftSpeed = throttle + steering; |
|
| 116 | - int rightSpeed = throttle - steering; |
|
| 117 | - |
|
| 118 | - leftSpeed = constrain(leftSpeed, -100, 100); |
|
| 119 | - rightSpeed = constrain(rightSpeed, -100, 100); |
|
| 120 | - |
|
| 121 | - // Debugging output |
|
| 122 | - static unsigned long lastDebug = 0; |
|
| 123 | - if (millis() - lastDebug > 100) { |
|
| 124 | - lastDebug = millis(); |
|
| 125 | - Serial.print("CH1: "); Serial.print(ch1); |
|
| 126 | - Serial.print(" | CH3: "); Serial.print(ch3); |
|
| 127 | - Serial.print(" | Thr: "); Serial.print(throttle); |
|
| 128 | - Serial.print(" | Str: "); Serial.print(steering); |
|
| 129 | - Serial.print(" | L: "); Serial.print(leftSpeed); |
|
| 130 | - Serial.print(" | R: "); Serial.println(rightSpeed); |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - driveMotor(M1_IN1, M1_IN2, leftSpeed); |
|
| 134 | - driveMotor(M2_IN1, M2_IN2, rightSpeed); |
|
| 135 | - } |
|
| 136 | - } |
|
| 137 | - } |
|
| 138 | -} |
code-dat/esp866-dat/printAllChannels/printAllChannels.ino
| ... | ... | @@ -1,37 +0,0 @@ |
| 1 | -#include <AlfredoCRSF.h> |
|
| 2 | -#include <SoftwareSerial.h> |
|
| 3 | - |
|
| 4 | -#define PIN_RX 13 |
|
| 5 | -#define PIN_TX 12 |
|
| 6 | - |
|
| 7 | -// Set up a new SoftwareSerial object |
|
| 8 | -SoftwareSerial crsfSerial(PIN_RX, PIN_TX); |
|
| 9 | -AlfredoCRSF crsf; |
|
| 10 | - |
|
| 11 | -void setup() |
|
| 12 | -{ |
|
| 13 | - Serial.begin(115200); |
|
| 14 | - Serial.println("COM Serial initialized"); |
|
| 15 | - |
|
| 16 | - crsfSerial.begin(115200); |
|
| 17 | - |
|
| 18 | - crsf.begin(crsfSerial); |
|
| 19 | -} |
|
| 20 | - |
|
| 21 | -void loop() |
|
| 22 | -{ |
|
| 23 | - // Must call crsf.update() in loop() to process data |
|
| 24 | - crsf.update(); |
|
| 25 | - printChannels(); |
|
| 26 | -} |
|
| 27 | - |
|
| 28 | -//Use crsf.getChannel(x) to get us channel values (1-16). |
|
| 29 | -void printChannels() |
|
| 30 | -{ |
|
| 31 | - for (int ChannelNum = 1; ChannelNum <= 16; ChannelNum++) |
|
| 32 | - { |
|
| 33 | - Serial.print(crsf.getChannel(ChannelNum)); |
|
| 34 | - Serial.print(", "); |
|
| 35 | - } |
|
| 36 | - Serial.println(" "); |
|
| 37 | -} |
|
| ... | ... | \ No newline at end of file |
code-dat/esp866-dat/soft-serial-debug-2/soft-serial-debug-2.ino
| ... | ... | @@ -1,82 +0,0 @@ |
| 1 | -#include <SoftwareSerial.h>
|
|
| 2 | -
|
|
| 3 | -// PIN 12 = RX, PIN 13 = TX
|
|
| 4 | -SoftwareSerial soft(12, 13);
|
|
| 5 | -
|
|
| 6 | -uint8_t buffer[64];
|
|
| 7 | -int bufPtr = 0;
|
|
| 8 | -unsigned long lastByteTime = 0;
|
|
| 9 | -unsigned long lastPrintTime = 0;
|
|
| 10 | -
|
|
| 11 | -void setup() {
|
|
| 12 | - Serial.begin(115200);
|
|
| 13 | - // Increase SoftwareSerial reliability by sticking to 115200
|
|
| 14 | - soft.begin(115200);
|
|
| 15 | - Serial.println("\n--- CRSF Debug V2 (Robust) ---");
|
|
| 16 | -}
|
|
| 17 | -
|
|
| 18 | -void loop() {
|
|
| 19 | - unsigned long now = millis();
|
|
| 20 | -
|
|
| 21 | - while (soft.available()) {
|
|
| 22 | - uint8_t b = soft.read();
|
|
| 23 | - lastByteTime = now;
|
|
| 24 | -
|
|
| 25 | - // 1. SYNC: Look for 0xC8 (Receiver Address)
|
|
| 26 | - if (bufPtr == 0) {
|
|
| 27 | - if (b == 0xC8) {
|
|
| 28 | - buffer[bufPtr++] = b;
|
|
| 29 | - }
|
|
| 30 | - }
|
|
| 31 | - // 2. LENGTH: Expecting 0x18 (24 bytes payload) for RC Channels
|
|
| 32 | - else if (bufPtr == 1) {
|
|
| 33 | - if (b == 0x18) {
|
|
| 34 | - buffer[bufPtr++] = b;
|
|
| 35 | - } else {
|
|
| 36 | - bufPtr = 0; // Invalid length for channels, restart
|
|
| 37 | - }
|
|
| 38 | - }
|
|
| 39 | - // 3. TYPE: Expecting 0x16 (RC Channels)
|
|
| 40 | - else if (bufPtr == 2) {
|
|
| 41 | - if (b == 0x16) {
|
|
| 42 | - buffer[bufPtr++] = b;
|
|
| 43 | - } else {
|
|
| 44 | - bufPtr = 0; // Not a channel frame
|
|
| 45 | - }
|
|
| 46 | - }
|
|
| 47 | - // 4. COLLECT DATA
|
|
| 48 | - else {
|
|
| 49 | - buffer[bufPtr++] = b;
|
|
| 50 | -
|
|
| 51 | - // CRSF RC packet is exactly 26 bytes: [Addr][Len][Type][22 bytes channels][CRC]
|
|
| 52 | - if (bufPtr >= 26) {
|
|
| 53 | - decodeAndPrint(buffer);
|
|
| 54 | - bufPtr = 0;
|
|
| 55 | - }
|
|
| 56 | - }
|
|
| 57 | - }
|
|
| 58 | -
|
|
| 59 | - // RECOVERY: If we got half a packet but no more data for 10ms, reset
|
|
| 60 | - if (bufPtr > 0 && (now - lastByteTime > 10)) {
|
|
| 61 | - bufPtr = 0;
|
|
| 62 | - }
|
|
| 63 | -}
|
|
| 64 | -
|
|
| 65 | -void decodeAndPrint(uint8_t* b) {
|
|
| 66 | - // Only print every 100ms to reduce CPU load and let SoftwareSerial run smoothly
|
|
| 67 | - if (millis() - lastPrintTime < 100) return;
|
|
| 68 | - lastPrintTime = millis();
|
|
| 69 | -
|
|
| 70 | - unsigned int ch[4];
|
|
| 71 | - // 11-bit decoding logic
|
|
| 72 | - ch[0] = (b[3] | b[4] << 8) & 0x07FF;
|
|
| 73 | - ch[1] = (b[4] >> 3 | b[5] << 5) & 0x07FF;
|
|
| 74 | - ch[2] = (b[5] >> 6 | b[6] << 2 | b[7] << 10) & 0x07FF;
|
|
| 75 | - ch[3] = (b[7] >> 1 | b[8] << 7) & 0x07FF;
|
|
| 76 | -
|
|
| 77 | - Serial.print("T:"); Serial.print(ch[2]); // Throttle usually CH3
|
|
| 78 | - Serial.print(" R:"); Serial.print(ch[3]); // Yaw/Rudder
|
|
| 79 | - Serial.print(" E:"); Serial.print(ch[1]); // Pitch/Elevator
|
|
| 80 | - Serial.print(" A:"); Serial.print(ch[0]); // Roll/Aileron
|
|
| 81 | - Serial.println();
|
|
| 82 | -} |
|
| ... | ... | \ No newline at end of file |
code-dat/esp866-dat/soft-serial-debug/soft-serial-debug.ino
| ... | ... | @@ -1,28 +0,0 @@ |
| 1 | -#include <SoftwareSerial.h>
|
|
| 2 | -
|
|
| 3 | -SoftwareSerial soft(13, 12); // RX, TX
|
|
| 4 | -
|
|
| 5 | -void setup() {
|
|
| 6 | - Serial.begin(115200);
|
|
| 7 | - soft.begin(115200);
|
|
| 8 | - Serial.println("\n--- CRSF Raw Hex Debugger ---");
|
|
| 9 | -}
|
|
| 10 | -
|
|
| 11 | -void loop() {
|
|
| 12 | - if (soft.available()) {
|
|
| 13 | - uint8_t incomingByte = soft.read();
|
|
| 14 | -
|
|
| 15 | - // Print as Hex
|
|
| 16 | - if (incomingByte < 0x10) Serial.print("0");
|
|
| 17 | - Serial.print(incomingByte, HEX);
|
|
| 18 | - Serial.print(" ");
|
|
| 19 | -
|
|
| 20 | - // Newline every 16 bytes
|
|
| 21 | - static int byteCount = 0;
|
|
| 22 | - byteCount++;
|
|
| 23 | - if (byteCount >= 16) {
|
|
| 24 | - Serial.println();
|
|
| 25 | - byteCount = 0;
|
|
| 26 | - }
|
|
| 27 | - }
|
|
| 28 | -} |
|
| ... | ... | \ No newline at end of file |