added robot code
parent
a8442a2ec0
commit
c31889efd9
|
@ -0,0 +1,19 @@
|
||||||
|
#include "Adafruit_VL53L0X.h"
|
||||||
|
Adafruit_VL53L0X lox = Adafruit_VL53L0X();
|
||||||
|
|
||||||
|
void initDistance(){
|
||||||
|
|
||||||
|
if (!lox.begin()) {
|
||||||
|
Serial.println(F("Failed to boot VL53L0X"));
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
lox.startRangeContinuous();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void updateDistance(){
|
||||||
|
if (lox.isRangeComplete()) {
|
||||||
|
Serial.print("Distance in mm: ");
|
||||||
|
Serial.println(lox.readRange());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
|
||||||
|
|
||||||
|
struct Encoder{
|
||||||
|
int rotations;
|
||||||
|
int currentAngle;
|
||||||
|
int estAngle;
|
||||||
|
bool threashhold;
|
||||||
|
|
||||||
|
float position(){
|
||||||
|
return rotations + (estAngle / 360.0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Encoder encoder;
|
||||||
|
|
||||||
|
|
||||||
|
void initEncoder(){
|
||||||
|
pinMode(A0, INPUT);
|
||||||
|
encoder.currentAngle = 0;
|
||||||
|
encoder.rotations = 0;
|
||||||
|
encoder.estAngle=0;
|
||||||
|
encoder.threashhold=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateEncoder(){
|
||||||
|
int rawIn = analogRead(A0);
|
||||||
|
// static long lastRan = 0;
|
||||||
|
// if(50>millis()-lastRan) return;
|
||||||
|
// lastRan = millis();
|
||||||
|
|
||||||
|
|
||||||
|
int rotation = map(rawIn, 4, 348, 0, 360);
|
||||||
|
int diff = rotation-encoder.currentAngle;
|
||||||
|
if(!encoder.threashhold && (abs(diff)>=50)){
|
||||||
|
encoder.threashhold=true;
|
||||||
|
if(diff<0){
|
||||||
|
encoder.rotations++;
|
||||||
|
encoder.estAngle = 0;
|
||||||
|
}
|
||||||
|
if(diff>0){
|
||||||
|
encoder.rotations--;
|
||||||
|
encoder.estAngle = 360;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(abs(diff)<30){
|
||||||
|
encoder.threashhold=false;
|
||||||
|
encoder.estAngle = rotation;
|
||||||
|
}
|
||||||
|
encoder.currentAngle = rotation;
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
#include "MPU6050_6Axis_MotionApps612.h"
|
||||||
|
//#include "MPU6050.h" // not necessary if using MotionApps include file
|
||||||
|
|
||||||
|
|
||||||
|
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
|
||||||
|
#include "Wire.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MPU6050 mpu;
|
||||||
|
|
||||||
|
// MPU control/status vars
|
||||||
|
bool dmpReady = false; // set true if DMP init was successful
|
||||||
|
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
|
||||||
|
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
|
||||||
|
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
|
||||||
|
uint16_t fifoCount; // count of all bytes currently in FIFO
|
||||||
|
uint8_t fifoBuffer[64]; // FIFO storage buffer
|
||||||
|
|
||||||
|
// orientation/motion vars
|
||||||
|
Quaternion q; // [w, x, y, z] quaternion container
|
||||||
|
VectorInt16 aa; // [x, y, z] accel sensor measurements
|
||||||
|
VectorInt16 gy; // [x, y, z] gyro sensor measurements
|
||||||
|
VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
|
||||||
|
VectorInt16 aaRealLast; // [x, y, z] gravity-free accel sensor measurements
|
||||||
|
VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements
|
||||||
|
VectorFloat gravity; // [x, y, z] gravity vector
|
||||||
|
float euler[3]; // [psi, theta, phi] Euler angle container
|
||||||
|
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
|
||||||
|
|
||||||
|
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
|
||||||
|
void ICACHE_RAM_ATTR dmpDataReady() {
|
||||||
|
mpuInterrupt = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INTERRUPT_PIN D7
|
||||||
|
|
||||||
|
void initGyro(){
|
||||||
|
// initialize device
|
||||||
|
Serial.println(F("Initializing I2C devices..."));
|
||||||
|
mpu.initialize();
|
||||||
|
pinMode(INTERRUPT_PIN, INPUT);
|
||||||
|
|
||||||
|
// verify connection
|
||||||
|
Serial.println(F("Testing device connections..."));
|
||||||
|
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
|
||||||
|
|
||||||
|
// wait for ready
|
||||||
|
// Serial.println(F("\nSend any character to begin DMP programming and demo: "));
|
||||||
|
// while (Serial.available() && Serial.read()); // empty buffer
|
||||||
|
// while (!Serial.available()); // wait for data
|
||||||
|
// while (Serial.available() && Serial.read()); // empty buffer again
|
||||||
|
|
||||||
|
// load and configure the DMP
|
||||||
|
Serial.println(F("Initializing DMP..."));
|
||||||
|
devStatus = mpu.dmpInitialize();
|
||||||
|
|
||||||
|
// supply your own gyro offsets here, scaled for min sensitivity
|
||||||
|
|
||||||
|
mpu.setXAccelOffset(-6009);
|
||||||
|
mpu.setYAccelOffset(-5577);
|
||||||
|
mpu.setZAccelOffset(7961);
|
||||||
|
mpu.setXGyroOffset(98);
|
||||||
|
mpu.setYGyroOffset(85);
|
||||||
|
mpu.setZGyroOffset(81);
|
||||||
|
// make sure it worked (returns 0 if so)
|
||||||
|
if (devStatus == 0) {
|
||||||
|
// Calibration Time: generate offsets and calibrate our MPU6050
|
||||||
|
// mpu.CalibrateAccel(6);
|
||||||
|
// mpu.CalibrateGyro(6);
|
||||||
|
Serial.println();
|
||||||
|
mpu.PrintActiveOffsets();
|
||||||
|
// turn on the DMP, now that it's ready
|
||||||
|
Serial.println(F("Enabling DMP..."));
|
||||||
|
mpu.setDMPEnabled(true);
|
||||||
|
|
||||||
|
// enable Arduino interrupt detection
|
||||||
|
Serial.print(F("Enabling interrupt detection (Arduino external interrupt "));
|
||||||
|
Serial.print(digitalPinToInterrupt(INTERRUPT_PIN));
|
||||||
|
Serial.println(F(")..."));
|
||||||
|
attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), dmpDataReady, RISING);
|
||||||
|
mpuIntStatus = mpu.getIntStatus();
|
||||||
|
|
||||||
|
// set our DMP Ready flag so the main loop() function knows it's okay to use it
|
||||||
|
Serial.println(F("DMP ready! Waiting for first interrupt..."));
|
||||||
|
dmpReady = true;
|
||||||
|
|
||||||
|
// get expected DMP packet size for later comparison
|
||||||
|
packetSize = mpu.dmpGetFIFOPacketSize();
|
||||||
|
} else {
|
||||||
|
// ERROR!
|
||||||
|
// 1 = initial memory load failed
|
||||||
|
// 2 = DMP configuration updates failed
|
||||||
|
// (if it's going to break, usually the code will be 1)
|
||||||
|
Serial.print(F("DMP Initialization failed (code "));
|
||||||
|
Serial.print(devStatus);
|
||||||
|
Serial.println(F(")"));
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool updateGyro(){
|
||||||
|
if (!dmpReady) return false;
|
||||||
|
if (mpu.dmpGetCurrentFIFOPacket(fifoBuffer)) {
|
||||||
|
// Xf = 1/4*(aaRealLast.x + aaReal.x)t^2 + Vot + Xo
|
||||||
|
// aaRealLast = aaReal;
|
||||||
|
|
||||||
|
mpu.dmpGetQuaternion(&q, fifoBuffer);
|
||||||
|
mpu.dmpGetAccel(&aa, fifoBuffer);
|
||||||
|
mpu.dmpGetGyro(&gy, fifoBuffer);
|
||||||
|
mpu.dmpGetGravity(&gravity, &q);
|
||||||
|
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
|
||||||
|
mpu.dmpGetEuler(euler, &q);
|
||||||
|
mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity);
|
||||||
|
mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
|
||||||
|
const char index_html[] PROGMEM = R"rawliteral(
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>PID Controller</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background-color: black;
|
||||||
|
color: greenyellow;
|
||||||
|
font-family: monospace;
|
||||||
|
display: flex;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#jsonOutput {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
#controls {
|
||||||
|
width: 50vw;
|
||||||
|
padding: 20px;
|
||||||
|
background: #222;
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
label, button {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.slider-value {
|
||||||
|
display: inline-block;
|
||||||
|
width: 40px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<pre id="jsonOutput">Loading...</pre>
|
||||||
|
<div id="controls">
|
||||||
|
<button onclick="setHome()">Set Home</button>
|
||||||
|
|
||||||
|
<h3>PID</h3>
|
||||||
|
<label for="pidIndex">Index:</label>
|
||||||
|
<select id="pidIndex">
|
||||||
|
<option value="0">angle</option>
|
||||||
|
<option value="1">pos</option>
|
||||||
|
<option value="2">turn</option>
|
||||||
|
</select>
|
||||||
|
<button onclick="fetchPID()">Get PID</button>
|
||||||
|
<label>Kp: <input type="range" id="kp" min="0" max="20" step="0.0001" oninput="updateValue('kp_value', this.value)"><span id="kp_value" class="slider-value">0</span></label>
|
||||||
|
<label>Ki: <input type="range" id="ki" min="0" max="120" step="0.0001" oninput="updateValue('ki_value', this.value)"><span id="ki_value" class="slider-value">0</span></label>
|
||||||
|
<label>Kd: <input type="range" id="kd" min="0" max="0.3" step="0.0001" oninput="updateValue('kd_value', this.value)"><span id="kd_value" class="slider-value">0</span></label>
|
||||||
|
<div>
|
||||||
|
<label>Direction:</label>
|
||||||
|
<label><input type="radio" name="direction" value="0" checked> Direct</label>
|
||||||
|
<label><input type="radio" name="direction" value="1"> Reverse</label>
|
||||||
|
</div>
|
||||||
|
<button onclick="setPID()">Set PID</button>
|
||||||
|
|
||||||
|
<label>Desired Yaw: <input type="range" id="gyro_kp" min="-180" max="180" step="5" oninput="updateValue('desired_yaw', this.value);setDesiredYaw(this.value)"><span id="desired_yaw" class="slider-value">0</span></label>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
async function fetchData(){
|
||||||
|
try{
|
||||||
|
const res = await fetch("get_stuff");
|
||||||
|
document.getElementById("jsonOutput").textContent = JSON.stringify(await res.json(), null, 2);
|
||||||
|
}catch(error){
|
||||||
|
document.getElementById("jsonOutput").textContent = "Error: " + error.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loopFetch(){
|
||||||
|
while(true){
|
||||||
|
await fetchData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loopFetch();
|
||||||
|
|
||||||
|
document.getElementById("pidIndex").addEventListener("change", (event) => {
|
||||||
|
fetchPID();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fetchPID() {
|
||||||
|
const index = parseInt(document.getElementById("pidIndex").value);
|
||||||
|
|
||||||
|
const res = await fetch("/get_pid", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({index})
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
document.getElementById("kp").value = data.kp;
|
||||||
|
document.getElementById("ki").value = data.ki;
|
||||||
|
document.getElementById("kd").value = data.kd;
|
||||||
|
updateValue('kp_value', data.kp);
|
||||||
|
updateValue('ki_value', data.ki);
|
||||||
|
updateValue('kd_value', data.kd);
|
||||||
|
document.querySelector(`input[name="direction"][value="${data.direction}"]`).checked = true;
|
||||||
|
}
|
||||||
|
window.onload = () => {
|
||||||
|
fetchPID();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setPID() {
|
||||||
|
const index = parseInt(document.getElementById("pidIndex").value);
|
||||||
|
|
||||||
|
const direction = document.querySelector('input[name="direction"]:checked').value;
|
||||||
|
const data = {
|
||||||
|
index,
|
||||||
|
kp: parseFloat(document.getElementById("kp").value),
|
||||||
|
ki: parseFloat(document.getElementById("ki").value),
|
||||||
|
kd: parseFloat(document.getElementById("kd").value),
|
||||||
|
direction: parseInt(direction)
|
||||||
|
};
|
||||||
|
await fetch("/set_pid", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDesiredYaw(yaw){
|
||||||
|
fetch("/set_desired_yaw", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({yaw})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGyroPID() {
|
||||||
|
const direction = document.querySelector('input[name="gyro_direction"]:checked').value;
|
||||||
|
const data = {
|
||||||
|
kp: parseFloat(document.getElementById("gyro_kp").value),
|
||||||
|
ki: parseFloat(document.getElementById("gyro_ki").value),
|
||||||
|
kd: parseFloat(document.getElementById("gyro_kd").value),
|
||||||
|
direction: parseInt(direction)
|
||||||
|
};
|
||||||
|
fetch("/set_gyro_pid", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateValue(id, value) {
|
||||||
|
document.getElementById(id).textContent = value;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)rawliteral";
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include <PID_v1.h>
|
||||||
|
|
||||||
|
//double angKp=3.5, angKi=80, angKd=0.042;
|
||||||
|
//double posKp=20, posKi=0.0, posKd=0.0;
|
||||||
|
//double turnKp=2, turnKi=0.0, turnKd=0.0;
|
||||||
|
|
||||||
|
|
||||||
|
double angKp=4.0, angKi=106.75, angKd=0.0472;
|
||||||
|
double posKp=0.96, posKi=1.28, posKd=0.5;
|
||||||
|
double turnKp=1, turnKi=5.0, turnKd=0.17;
|
||||||
|
|
||||||
|
|
||||||
|
double angleInput, angleOutput, angleSetpoint;
|
||||||
|
PID anglePID(&angleInput, &angleOutput, &angleSetpoint, angKp, angKi, angKd, AUTOMATIC);
|
||||||
|
|
||||||
|
double posInput, posOutput, posSetpoint;
|
||||||
|
PID posPID(&posInput, &posOutput, &posSetpoint, posKp, posKi, posKd, AUTOMATIC);
|
||||||
|
|
||||||
|
double turnInput, turnOutput, turnSetpoint;
|
||||||
|
PID turnPID(&turnInput, &turnOutput, &turnSetpoint, turnKp, turnKi, turnKd, AUTOMATIC);
|
||||||
|
|
||||||
|
PID* pids[] = {&anglePID, &posPID, &turnPID};
|
||||||
|
|
||||||
|
void initPID(){
|
||||||
|
angleSetpoint = 0;
|
||||||
|
anglePID.SetOutputLimits(-180, 180);
|
||||||
|
anglePID.SetMode(AUTOMATIC);
|
||||||
|
anglePID.SetSampleTime(5);
|
||||||
|
|
||||||
|
posSetpoint = 0.5;
|
||||||
|
posPID.SetOutputLimits(-1, 1);
|
||||||
|
posPID.SetMode(AUTOMATIC);
|
||||||
|
posPID.SetSampleTime(5);
|
||||||
|
posPID.SetControllerDirection(DIRECT);
|
||||||
|
|
||||||
|
turnSetpoint = 0;
|
||||||
|
turnPID.SetOutputLimits(-15, 15);
|
||||||
|
turnPID.SetMode(AUTOMATIC);
|
||||||
|
turnPID.SetSampleTime(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Speeds{
|
||||||
|
float left;
|
||||||
|
float right;
|
||||||
|
};
|
||||||
|
|
||||||
|
Speeds updatePID(){
|
||||||
|
posPID.Compute();
|
||||||
|
angleSetpoint = posOutput;
|
||||||
|
anglePID.Compute();
|
||||||
|
|
||||||
|
float maxTurn = max(0.0f, 15.0f-abs((float)angleOutput));
|
||||||
|
turnPID.SetOutputLimits(-maxTurn, maxTurn);
|
||||||
|
turnSetpoint = desiredYaw;
|
||||||
|
turnInput = currentYaw;
|
||||||
|
turnPID.Compute();
|
||||||
|
|
||||||
|
Speeds speeds;
|
||||||
|
speeds.left = (float)(angleOutput + turnOutput);
|
||||||
|
speeds.right = (float)(-angleOutput + turnOutput);
|
||||||
|
if(angleInput>20 || angleInput<-20){
|
||||||
|
speeds.left = 0;
|
||||||
|
speeds.right = 0;
|
||||||
|
}
|
||||||
|
return speeds;
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
|
||||||
|
#include "I2Cdev.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include <Servo.h>
|
||||||
|
Servo left;
|
||||||
|
Servo right;
|
||||||
|
|
||||||
|
float angleOffset = -2.4241745;
|
||||||
|
float desiredYaw = 0.0;
|
||||||
|
float currentYaw = 0.0;
|
||||||
|
|
||||||
|
struct DebugState{
|
||||||
|
int motorTargetAngle;
|
||||||
|
};
|
||||||
|
|
||||||
|
DebugState dbgState;
|
||||||
|
|
||||||
|
#include "distance.h"
|
||||||
|
#include "encoder.h"
|
||||||
|
#include "gyro.h"
|
||||||
|
#include "pid.h"
|
||||||
|
#include "webserver.h"
|
||||||
|
|
||||||
|
|
||||||
|
void initSerial(){
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial);
|
||||||
|
delay(300);
|
||||||
|
Serial.print("\n\n\n\n\n\n\n\n\n\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void initMotors(){
|
||||||
|
left.attach(D3);
|
||||||
|
right.attach(D4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void initI2C(){
|
||||||
|
digitalWrite(D2, LOW);
|
||||||
|
digitalWrite(D1, LOW);
|
||||||
|
delay(100);
|
||||||
|
Wire.begin();
|
||||||
|
Wire.setClock(400000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
initSerial();
|
||||||
|
initWifi(false);
|
||||||
|
initServer();
|
||||||
|
initMotors();
|
||||||
|
initPID();
|
||||||
|
initI2C();
|
||||||
|
initEncoder();
|
||||||
|
initDistance();
|
||||||
|
initGyro();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if (updateGyro()) { //gyro data
|
||||||
|
currentYaw=ypr[0]*180/M_PI;
|
||||||
|
double angle = ypr[1] * 180 / M_PI;
|
||||||
|
if(angle>180)
|
||||||
|
angle -= 180;
|
||||||
|
angleInput = angle + angleOffset;
|
||||||
|
}
|
||||||
|
{// encoder data
|
||||||
|
updateEncoder();
|
||||||
|
posInput = encoder.position();
|
||||||
|
}
|
||||||
|
updateDistance();
|
||||||
|
|
||||||
|
|
||||||
|
Speeds speeds = updatePID();
|
||||||
|
|
||||||
|
speeds.left = min(90.0f-10, max(-90.0f+10, speeds.left));
|
||||||
|
speeds.right = min(90.0f-10, max(-90.0f+10, speeds.right));
|
||||||
|
left.write(90+(int)speeds.left);
|
||||||
|
right.write(90+(int)speeds.right);
|
||||||
|
|
||||||
|
delay(5);
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <ESPAsyncTCP.h>
|
||||||
|
#include <ESPAsyncWebServer.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <AsyncJson.h>
|
||||||
|
#include <AsyncMessagePack.h>
|
||||||
|
|
||||||
|
#include "page_html.h"
|
||||||
|
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
void initWifi(bool host){
|
||||||
|
if(host){
|
||||||
|
WiFi.softAP("MEOW");
|
||||||
|
IPAddress IP = WiFi.softAPIP();
|
||||||
|
Serial.print("AP IP address: ");
|
||||||
|
Serial.println(IP);
|
||||||
|
}else{
|
||||||
|
Serial.print("Connecting to ");
|
||||||
|
Serial.println("Michael Loves CP");
|
||||||
|
WiFi.enableInsecureWEP(false);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.setPhyMode(WIFI_PHY_MODE_11G);
|
||||||
|
WiFi.begin("Michael Loves CP", "cockandpavly20");
|
||||||
|
while(WiFi.status() != WL_CONNECTED){
|
||||||
|
delay(500);
|
||||||
|
Serial.print('.');
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FVec3{
|
||||||
|
float x,y,z;
|
||||||
|
};
|
||||||
|
struct FVec4{
|
||||||
|
float x,y,z;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Everything{
|
||||||
|
float motorTargetAngle;
|
||||||
|
float position;
|
||||||
|
FVec3 anglePID;
|
||||||
|
FVec3 posPID;
|
||||||
|
FVec3 ypr;
|
||||||
|
FVec3 euler;
|
||||||
|
FVec3 gravity;
|
||||||
|
FVec4 q;
|
||||||
|
FVec3 aa;
|
||||||
|
FVec3 gy;
|
||||||
|
FVec3 aaReal;
|
||||||
|
FVec3 aaWorld;
|
||||||
|
};
|
||||||
|
|
||||||
|
void initServer(){
|
||||||
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
|
request->send_P(200, "text/html", index_html);
|
||||||
|
});
|
||||||
|
server.on("/get_stuff_bin", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
|
float arr[] = {
|
||||||
|
dbgState.motorTargetAngle,
|
||||||
|
encoder.position(),
|
||||||
|
angleSetpoint, angleInput, angleOutput,
|
||||||
|
posSetpoint, posInput, posOutput,
|
||||||
|
ypr[0]*180/M_PI, ypr[1]*180/M_PI, ypr[2]*180/M_PI,
|
||||||
|
euler[0]*180/M_PI, euler[1]*180/M_PI, euler[2]*180/M_PI,
|
||||||
|
gravity.x, gravity.y, gravity.z,
|
||||||
|
q.w, q.x, q.y, q.z,
|
||||||
|
aa.x, aa.y, aa.z,
|
||||||
|
gy.x, gy.y, gy.z,
|
||||||
|
aaReal.x, aaReal.y, aaReal.z,
|
||||||
|
aaWorld.x, aaWorld.y, aaWorld.z
|
||||||
|
};
|
||||||
|
request->send(200, "application/text", (const uint8_t*)arr, sizeof(arr)*4);
|
||||||
|
});
|
||||||
|
server.on("/get_stuff", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||||
|
char buff[1024];
|
||||||
|
int ret = snprintf(buff, sizeof(buff),
|
||||||
|
R"({
|
||||||
|
"motorTargetAngle": %d,
|
||||||
|
"position": %f,
|
||||||
|
"anglePID": {"setpoint": %lf, "input": %lf, "output": %lf},
|
||||||
|
"posPID": {"setpoint": %lf, "input": %lf, "output": %lf},
|
||||||
|
"turnPID": {"setpoint": %lf, "input": %lf, "output": %lf},
|
||||||
|
"ypr": {"yaw": %f, "pitch": %f, "roll": %f},
|
||||||
|
"euler": {"psi": %f, "theta": %f, "phi": %f},
|
||||||
|
"gravity": {"x": %f, "y": %f, "z": %f},
|
||||||
|
"q": {"w": %f, "x": %f, "y": %f, "z": %f},
|
||||||
|
"aa": {"x": %hd, "y": %hd, "z": %hd},
|
||||||
|
"gy": {"x": %hd, "y": %hd, "z": %hd},
|
||||||
|
"aaReal": {"x": %hd, "y": %hd, "z": %hd},
|
||||||
|
"aaWorld": {"x": %hd, "y": %hd, "z": %hd}
|
||||||
|
})",
|
||||||
|
dbgState.motorTargetAngle,
|
||||||
|
encoder.position(),
|
||||||
|
angleSetpoint, angleInput, angleOutput,
|
||||||
|
posSetpoint, posInput, posOutput,
|
||||||
|
turnSetpoint, turnInput, turnOutput,
|
||||||
|
ypr[0]*180/M_PI, ypr[1]*180/M_PI, ypr[2]*180/M_PI,
|
||||||
|
euler[0]*180/M_PI, euler[1]*180/M_PI, euler[2]*180/M_PI,
|
||||||
|
gravity.x, gravity.y, gravity.z,
|
||||||
|
q.w, q.x, q.y, q.z,
|
||||||
|
aa.x, aa.y, aa.z,
|
||||||
|
gy.x, gy.y, gy.z,
|
||||||
|
aaReal.x, aaReal.y, aaReal.z,
|
||||||
|
aaWorld.x, aaWorld.y, aaWorld.z
|
||||||
|
);
|
||||||
|
request->send(200, "application/json", buff);
|
||||||
|
});
|
||||||
|
server.on("/get_pid", HTTP_POST, [](AsyncWebServerRequest *request){}, NULL,
|
||||||
|
[](AsyncWebServerRequest *request, uint8_t *bodyData, size_t bodyLen, size_t index, size_t total) {
|
||||||
|
StaticJsonDocument<256> json;
|
||||||
|
deserializeJson(json, &bodyData[index], bodyLen);
|
||||||
|
int idx = json["index"];
|
||||||
|
if(idx>3){
|
||||||
|
request->send(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PID& pid = *pids[idx];
|
||||||
|
char buff[256];
|
||||||
|
int ret = snprintf(buff, sizeof(buff),
|
||||||
|
R"({"kp": %lf, "ki": %lf, "kd": %lf, "direction": %d})",
|
||||||
|
pid.GetKp(), pid.GetKi(), pid.GetKd(), pid.GetDirection()
|
||||||
|
);
|
||||||
|
request->send(200, "application/json", buff);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/set_pid", HTTP_POST, [](AsyncWebServerRequest *request){}, NULL,
|
||||||
|
[](AsyncWebServerRequest *request, uint8_t *bodyData, size_t bodyLen, size_t index, size_t total) {
|
||||||
|
StaticJsonDocument<256> json;
|
||||||
|
deserializeJson(json, &bodyData[index], bodyLen);
|
||||||
|
int idx = json["index"];
|
||||||
|
if(idx>3){
|
||||||
|
request->send(400);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PID& pid = *pids[idx];
|
||||||
|
if(json.containsKey("kp"))
|
||||||
|
pid.SetTunings(json["kp"], json["ki"], json["kd"]);
|
||||||
|
if(json.containsKey("direction"))
|
||||||
|
pid.SetControllerDirection(json["direction"]);
|
||||||
|
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("/set_desired_yaw", HTTP_POST, [](AsyncWebServerRequest *request){}, NULL,
|
||||||
|
[](AsyncWebServerRequest *request, uint8_t *bodyData, size_t bodyLen, size_t index, size_t total) {
|
||||||
|
StaticJsonDocument<256> json;
|
||||||
|
deserializeJson(json, &bodyData[index], bodyLen);
|
||||||
|
desiredYaw = json["yaw"];
|
||||||
|
Serial.print(desiredYaw);
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.begin();
|
||||||
|
}
|
Loading…
Reference in New Issue