├── .gitignore ├── .vscode ├── arduino.json └── settings.json ├── ArduinoSatelliteTracker.ino ├── README.md ├── analog.cpp ├── analog.h ├── app.cpp ├── app.h ├── display.cpp ├── display.h ├── eeprom.cpp ├── eeprom.h ├── html.h ├── images ├── 20240222_111427.jpg ├── 20240222_111456.jpg ├── 20240223_172119.jpg ├── 20240223_172133.jpg ├── Screenshot_20240223_172631_Chrome.jpg ├── Screenshot_20240223_172711_Chrome.jpg ├── Screenshot_20240223_172840_Chrome.jpg ├── Screenshot_20240223_172920_Chrome.jpg └── wiring.jpg ├── mpu6050.cpp ├── mpu6050.h ├── mpu9250.cpp ├── mpu9250.h ├── network.cpp ├── network.h ├── ntp.cpp ├── ntp.h ├── tracker.cpp ├── tracker.h ├── webserver.cpp ├── webserver.h ├── websocket.cpp └── websocket.h /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/c_cpp_properties.json 2 | build -------------------------------------------------------------------------------- /.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "board": "esp8266:esp8266:d1_mini_clone", 3 | "port": "COM4", 4 | "sketch": "ArduinoSatelliteTracker.ino", 5 | "output": "./build" 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "arduino.useArduinoCli": true, 3 | "arduino.logLevel": "info", 4 | "arduino.allowPDEFiletype": false, 5 | "arduino.enableUSBDetection": true, 6 | "arduino.disableTestingOpen": false, 7 | "arduino.skipHeaderProvider": false, 8 | "arduino.additionalUrls": [ 9 | "http://arduino.esp8266.com/stable/package_esp8266com_index.json" 10 | ], 11 | } -------------------------------------------------------------------------------- /ArduinoSatelliteTracker.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Author: 3 | - Philip Bordado (kerpz@yahoo.com) 4 | 5 | Hardware: 6 | - Wemos D1 mini (Compatible board) 7 | - MPU9250 / MPU6050 (not used) 8 | - L298 Dual H Bridge (for motor control) 9 | 10 | Software: 11 | - Arduino 1.8.19 (Stable) 12 | - Board 3.0.2 13 | - Adafruit SSD1306 2.5.3 14 | - SparkFun SGP4 1.0.3 15 | - NTPClient 3.2.1 16 | - ArduinoJson 6.21.5 17 | - Websockets 2.4.0 18 | */ 19 | 20 | #include "eeprom.h" 21 | #include "network.h" 22 | #include "webserver.h" 23 | // #include "websocket.h" 24 | #include "app.h" 25 | 26 | void setup() 27 | { 28 | delay(10); 29 | 30 | Serial.begin(115200); 31 | 32 | Serial.println(); 33 | Serial.println(APPNAME); 34 | 35 | loadConfig(); 36 | 37 | networkSetup(); 38 | webserverSetup(); 39 | 40 | appSetup(); 41 | 42 | delay(100); 43 | } 44 | 45 | void loop() 46 | { 47 | networkLoop(); 48 | webserverLoop(); 49 | 50 | appLoop(); 51 | } 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Arduino Satellite Tracker 2 | 3 | The purpose of this project is to automatically track satellite and perform amateur activities like voice or telemetry. Use case includes tracking of objects in the sky like the sun or perhaps a planet. 4 | 5 | # Materials 6 | 7 | |Quantity|Description|Link| 8 | |---|----|----| 9 | |1|Wemos D1 mini| 10 | ||Proto PCB and Wires/Connectors| 11 | |2|12v 0.6rpm Torque Turbo Worm Motor 370 Right Angle|[Shopee](https://shopee.ph/%E2%9C%BFtocawe-DC-12V-0.6RPM-High-torque-Turbo-Worm-Electric-Geared-DC-Motor-GW370-Low-Speed-i.96685751.1684854369)| 12 | |1|L298N Dual H Bridge Stepper Motor Controller (module)|[Shopee](https://shopee.ph/L298-New-Dual-H-Bridge-DC-Stepper-Motor-Drive-Controller-Board-Module-L298N-for-Arduino-stepper-motor-smart-car-robot-i.307295456.7351756627)| 13 | |1|6mm Rigid Flange Couppler|[Shopee](https://shopee.ph/Emprichman%E2%9D%A6-3-4-5-6-7-8-10Mm-Rigid-Flange-Coupling-Motor-Guide-Shaft-Coupler-Motor-Connecto-0-0-0-0-0-i.289645126.7650415550)| 14 | |1|6mm Rigid Shaft Coupling|[Shopee](https://shopee.ph/COD-2-3-4-5-6-7-8mm-Rigid-Shaft-Coupling-Motor-Coupler-with-Spanner-for-RC-Boat-Car-i.58547667.11732017330)| 15 | |1|Enclosure 158x90x65 mm|[Shopee](https://shopee.ph/JANE-ABS-Enclosure-Box-Plastic-Electronic-Boxes-Project-Instrument-Case-Parts-Accessories-Waterproof-Housing-Grey-White-Outdoor-Junction-Holder-i.50706257.9104114861)| 16 | |1|Tripod|[Shopee](https://shopee.ph/Selfie-Ring-light-Stand-Only-i.66518620.7158120434)| 17 | 18 | Referencce: [My Google Sheet List](https://docs.google.com/spreadsheets/d/1cMQumLH8W8JSPdNhxq0K1dAPo-xYCWljrY0g2Y8n2QI) 19 | 20 | Wiring for L298N 21 | -------------------- 22 | L297N Wemos D1 mini 23 | P1 ---------------------- D5 24 | P2 ---------------------- D6 25 | P3 ---------------------- D7 26 | P4 ---------------------- D8 27 | 28 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/wiring.jpg "Wiring") 29 | 30 | # Building / Compiling 31 | 32 | Code Editor 33 | - Visual Studio Code 34 | 35 | Visual Studio Code Extensions 36 | - Arduino 37 | - C/C++ 38 | - C/C++ Extension Pack 39 | - C/C++ Themes 40 | - CMake Tools 41 | 42 | Uploadable Binary File via Webmin 43 | - build/ArduinoStalliteTracker.ino.bin 44 | 45 | Alternative (the easy way) 46 | - Arduino IDE 47 | 48 | # Device Web Screenshots 49 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/Screenshot_20240223_172631_Chrome.jpg "Main Page") 50 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/Screenshot_20240223_172711_Chrome.jpg "Config Page") 51 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/Screenshot_20240223_172840_Chrome.jpg "Tracker Page") 52 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/Screenshot_20240223_172920_Chrome.jpg "App Page") 53 | 54 | # Device Actual Photos 55 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/20240223_172119.jpg) 56 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/20240223_172133.jpg?raw=true) 57 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/20240222_111427.jpg) 58 | ![Alt text](https://github.com/kerpz/ArduinoSatelliteTracker/blob/main/images/20240222_111456.jpg) 59 | 60 | # TODO 61 | 62 | - MCU Flasher (like NodeMCU Flasher) 63 | - User manual 64 | - IMU integration 65 | - GPS integration 66 | 67 | # Reference 68 | 69 | [Sarcnet mk1b](https://www.sarcnet.org/rotator-mk1.html) 70 | -------------------------------------------------------------------------------- /analog.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "eeprom.h" 4 | #include "analog.h" 5 | /* https://www.allaboutcircuits.com/tools/voltage-divider-calculator 6 | * source Voltage = max voltage of the battery 7 | * R2 = 100k (builtin on wemos D1 mini) 8 | * R1 = 220k + additional resistance (100k for 4.2v, 1.22m for 16v) 9 | * target volatge output should be 1v 10 | * 1.0 * (R1 + R2) / R2 11 | */ 12 | 13 | // ADC specs for wemos d1 mini 14 | #define ADC_BITS 10 15 | #define ADC_COUNTS (1 << ADC_BITS) 16 | double offsetI = ADC_COUNTS >> 1; 17 | // #define CT_CALIBRATION 30.0 18 | 19 | int samples = 1480; 20 | int SupplyVoltage = 3200; 21 | 22 | float a_voltage = 0.0; 23 | float a_current = 0.0; 24 | float a_power = 0.0; 25 | 26 | void analogSetup() 27 | { 28 | } 29 | 30 | void analogLoop() 31 | { 32 | static float avg_power = 0.00; 33 | // setup for volt meter reading with 1.2M resistor 34 | int raw = analogRead(A0); 35 | a_voltage = raw / 1023.0; 36 | a_voltage = a_voltage * 15.4; 37 | 38 | // setup for ct sensor reading with 30.0 amps max 39 | /* 40 | double sumI, sqI, filteredI; 41 | for (unsigned int n = 0; n < samples; n++) 42 | { 43 | int sampleI = analogRead(A0); 44 | offsetI = (offsetI + (sampleI - offsetI) / 1024); 45 | filteredI = sampleI - offsetI; 46 | sqI = filteredI * filteredI; 47 | sumI += sqI; 48 | } 49 | double I_RATIO = ct_calibration * ((SupplyVoltage / 1000.0) / (ADC_COUNTS)); 50 | 51 | a_voltage = ct_voltage; 52 | a_current = I_RATIO * sqrt(sumI / samples); 53 | 54 | a_power = (a_current * a_voltage * ct_pf); // watts / power apparent 55 | 56 | // moving average 57 | a_power = (avg_power + a_power) / 2; 58 | avg_power = a_power; 59 | */ 60 | } 61 | -------------------------------------------------------------------------------- /analog.h: -------------------------------------------------------------------------------- 1 | #ifndef ANALOG_H 2 | #define ANALOG_H 3 | 4 | extern float a_voltage; 5 | extern float a_current; 6 | extern float a_power; 7 | 8 | void analogSetup(); 9 | void analogLoop(); 10 | 11 | #endif -------------------------------------------------------------------------------- /app.cpp: -------------------------------------------------------------------------------- 1 | // wifi 2 | #include 3 | 4 | #include "app.h" 5 | 6 | // const int sclPin = D1; // SCL 7 | // const int sdaPin = D2; // SDA 8 | // const int out1Control = D3; // use pnp // pulled up 2n3906 9 | // const int out2Control = D4; // use pnp pulled up 2n3906 10 | // L298 Dual H Bridge motor pins 11 | const int azFwdPin = D5; 12 | const int azRevPin = D6; 13 | const int elFwdPin = D7; 14 | const int elRevPin = D8; 15 | 16 | // motor specs 17 | // 0.6 RPM 12v Torque Worm 18 | // 0.6 RPM = 3.6 Deg/s 19 | // 278 ms/Deg 20 | 21 | // az 0 to 360, el 0 to 90 22 | // yaw,roll -180 to 180, pitch -90 to 90 23 | 24 | uint32_t targetTimeAz = 0; // ms 25 | uint32_t targetTimeEl = 0; // ms 26 | 27 | // calibrate Az 360 degrees? 28 | // uint8_t tmp_degree = 0; 29 | // unsigned long tmp_start_ms; // ms 30 | // float cal_msPerDeg; // ms 31 | uint32_t debug_ms = 0; 32 | float debug_deg = 0.0; 33 | 34 | // global 35 | uint8_t motor_mode_az = 0; 36 | float motor_az = 0.0; 37 | uint8_t motor_mode_el = 0; 38 | float motor_el = 0.0; 39 | 40 | uint16_t run_time = 0; 41 | 42 | // timing 43 | int timezone = 8; 44 | uint32_t epoch = 0; 45 | uint8_t second = 0; 46 | uint8_t minute = 0; 47 | uint8_t hour = 0; 48 | uint8_t day = 0; 49 | uint8_t month = 0; 50 | uint16_t year = 0; 51 | 52 | void stopAz() 53 | { 54 | digitalWrite(azFwdPin, LOW); 55 | digitalWrite(azRevPin, LOW); 56 | Serial.println("stopAz"); 57 | motor_mode_az = 0; 58 | targetTimeAz = 0; 59 | // cal_msPerDeg = (millis() - tmp_start_ms) / 360; // calculate // calibration only 60 | } 61 | 62 | void stopEl() 63 | { 64 | digitalWrite(elFwdPin, LOW); 65 | digitalWrite(elRevPin, LOW); 66 | Serial.println("stopEl"); 67 | motor_mode_el = 0; 68 | targetTimeEl = 0; 69 | } 70 | 71 | void forwardAz() 72 | { 73 | // tmp_start_ms = millis(); // save start // msPerDeg calibration only 74 | digitalWrite(azFwdPin, LOW); 75 | digitalWrite(azRevPin, HIGH); 76 | Serial.println("forwardAz"); 77 | motor_mode_az = 1; 78 | } 79 | 80 | void reverseAz() 81 | { 82 | digitalWrite(azFwdPin, HIGH); 83 | digitalWrite(azRevPin, LOW); 84 | Serial.println("reverseAz"); 85 | motor_mode_az = 2; 86 | } 87 | 88 | void forwardEl() 89 | { 90 | digitalWrite(elFwdPin, LOW); 91 | digitalWrite(elRevPin, HIGH); 92 | Serial.println("forwardEl"); 93 | motor_mode_el = 1; 94 | } 95 | 96 | void reverseEl() 97 | { 98 | digitalWrite(elFwdPin, HIGH); 99 | digitalWrite(elRevPin, LOW); 100 | Serial.println("reverseEl"); 101 | motor_mode_el = 2; 102 | } 103 | 104 | void setAz(int deg) // 0 - 360 105 | { 106 | if (deg >= 0 && deg <= 360 && motor_mode_az == 0) 107 | { 108 | int t_deg = motor_az - deg; 109 | 110 | uint32_t ms = abs(t_deg) * ms_per_deg; 111 | targetTimeAz = millis() + ms; 112 | 113 | if (t_deg < 0) 114 | forwardAz(); 115 | else if (t_deg > 0) 116 | reverseAz(); 117 | 118 | motor_az = deg; 119 | } 120 | } 121 | 122 | void setEl(int deg) // 0 - 90 123 | { 124 | if (deg >= 0 && deg <= 90 && motor_mode_el == 0) 125 | { 126 | int t_deg = motor_el - deg; 127 | 128 | uint32_t ms = abs(t_deg) * ms_per_deg; 129 | targetTimeEl = millis() + ms; 130 | 131 | if (t_deg < 0) 132 | forwardEl(); 133 | else if (t_deg > 0) 134 | reverseEl(); 135 | 136 | motor_el = deg; 137 | } 138 | } 139 | 140 | void appSetup() 141 | { 142 | pinMode(azFwdPin, OUTPUT); 143 | pinMode(azRevPin, OUTPUT); 144 | pinMode(elFwdPin, OUTPUT); 145 | pinMode(elRevPin, OUTPUT); 146 | 147 | stopAz(); 148 | stopEl(); 149 | 150 | // get from sensor, or manually fixed set 151 | motor_az = 0.0; // pointing north 152 | motor_el = 0.0; // pointing horizon 153 | 154 | getTLE("25544"); 155 | delay(100); 156 | trackerSetup(); 157 | 158 | mpu9250Setup(); 159 | } 160 | 161 | void appLoop() 162 | { 163 | static uint32_t msTick = millis(); 164 | 165 | if (millis() - msTick >= 1000) // 1000ms refresh rate 166 | { 167 | msTick = millis(); 168 | 169 | ntpLoop(); 170 | 171 | if (analog_enable) 172 | analogLoop(); 173 | 174 | if (tracker_enable) 175 | trackerLoop(); 176 | 177 | if (mpu9250_enable) 178 | mpu9250Loop(); 179 | 180 | if (second >= 59) 181 | { 182 | if (run_time < 65500) 183 | run_time++; 184 | 185 | // if (post_enable) 186 | // postLoop(); 187 | second = 0; 188 | 189 | /* 190 | if (minute >= 59) 191 | { 192 | minute = 0; 193 | if (hour >= 23) 194 | { 195 | hour = 0; 196 | } 197 | else 198 | hour++; 199 | } 200 | else 201 | minute++; 202 | */ 203 | } 204 | else 205 | second++; 206 | } 207 | 208 | if (motor_mode_az > 0 && targetTimeAz != 0 && targetTimeAz <= millis()) 209 | stopAz(); // stop 210 | if (motor_mode_el > 0 && targetTimeEl != 0 && targetTimeEl <= millis()) 211 | stopEl(); // stop 212 | } 213 | -------------------------------------------------------------------------------- /app.h: -------------------------------------------------------------------------------- 1 | #ifndef APP_H 2 | #define APP_H 3 | 4 | #include "eeprom.h" 5 | #include "ntp.h" 6 | #include "analog.h" 7 | #include "display.h" 8 | // #include "post.h" 9 | #include "tracker.h" 10 | #include "mpu9250.h" 11 | 12 | #define APPNAME "Satellite v1.0" 13 | #define APPCODE "sat" 14 | 15 | #define APssid "Satellite-AP" 16 | #define APpassword "12345678" 17 | 18 | // motor part 19 | extern uint8_t motor_mode_az; 20 | extern float motor_az; 21 | extern uint8_t motor_mode_el; 22 | extern float motor_el; 23 | 24 | // extern float cal_msPerDeg; 25 | // extern uint32_t debug_ms; 26 | // extern float debug_deg; 27 | 28 | extern uint16_t run_time; 29 | 30 | // timing 31 | extern int timezone; 32 | extern uint32_t epoch; 33 | extern uint8_t second; 34 | extern uint8_t minute; 35 | extern uint8_t hour; 36 | extern uint8_t day; 37 | extern uint8_t month; 38 | extern uint16_t year; 39 | 40 | // for manual control / calibration 41 | void stopAz(); 42 | void stopEl(); 43 | void forwardAz(); 44 | void reverseAz(); 45 | void forwardEl(); 46 | void reverseEl(); 47 | 48 | void setAz(int deg); 49 | void setEl(int deg); 50 | 51 | void appSetup(); 52 | void appLoop(); 53 | 54 | #endif -------------------------------------------------------------------------------- /display.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "display.h" 4 | 5 | #define SCREEN_WIDTH 128 // OLED display width, in pixels 6 | #define SCREEN_HEIGHT 64 // OLED display height, in pixels 7 | // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) 8 | #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) 9 | 10 | Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); 11 | 12 | void displaySetup() 13 | { 14 | Serial.print("Starting Display ... "); 15 | display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 16 | Serial.println("Done"); 17 | 18 | display.clearDisplay(); 19 | display.setTextSize(2); // Draw 2X-scale text 20 | display.setTextColor(SSD1306_WHITE); 21 | 22 | display.clearDisplay(); 23 | display.setCursor(0, 0); 24 | display.print(APPNAME); 25 | display.display(); 26 | } 27 | 28 | void displayLoop() 29 | { 30 | // static unsigned long msTick = millis(); 31 | // if (millis() - msTick >= 500) // 500ms refresh rate 32 | //{ 33 | // msTick = millis(); 34 | 35 | display.clearDisplay(); 36 | display.setCursor(0, 0); 37 | display.print("Y: "); 38 | display.println(yaw); 39 | display.print("P: "); 40 | display.println(pitch); 41 | display.print("R: "); 42 | display.println(roll); 43 | display.print("T: "); 44 | display.println(temperature); 45 | display.display(); 46 | //} 47 | } 48 | -------------------------------------------------------------------------------- /display.h: -------------------------------------------------------------------------------- 1 | #ifndef DISPLAY_H 2 | #define DISPLAY_H 3 | 4 | #include "mpu9250.h" 5 | #include "app.h" 6 | 7 | void displaySetup(); 8 | void displayLoop(); 9 | 10 | #endif -------------------------------------------------------------------------------- /eeprom.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | // eeprom 3 | #include 4 | 5 | #include "eeprom.h" 6 | 7 | // globals 8 | char ssid[32] = "KERPZ-AP2"; // limit to 32 bytes 9 | char password[32] = "hackmelol"; // limit to 32 bytes 10 | 11 | // peripheral devices 12 | uint8_t beep_enable = 0; 13 | uint8_t analog_enable = 0; 14 | uint8_t display_enable = 0; 15 | uint8_t ads1115_enable = 0; 16 | uint8_t mpu9250_enable = 0; 17 | uint8_t motor_enable = 1; 18 | uint8_t tracker_enable = 1; 19 | 20 | float ms_per_deg = 277.778; 21 | 22 | // tracker location 23 | float latitude = 14.6112342; 24 | float longitude = 121.1303641; 25 | float altitude = 19.0; 26 | float declination = -2.57; // Cainta // dd = d + m/60 + s/3600 27 | 28 | char tle_name[32] = "ISS (ZARYA)"; 29 | char tle_line1[70] = "1 25544U 98067A 24051.89611201 .00017355 00000+0 30940-3 0 9999"; 30 | char tle_line2[70] = "2 25544 51.6402 176.6243 0001894 294.5134 162.9214 15.50183938440364"; 31 | 32 | uint8_t auto_control = 0; 33 | 34 | // uint8_t post_enable = 0; 35 | // char api_key[] = "NIJCG7UI28O9CAYD"; // limit to 32 36 | // char api_url[] = "https://192.168.2.1:8001/cgi-bin/custom-full.cgi?a=solard"; // limit to 256 37 | // uint16_t http_timeout = 0; 38 | 39 | void loadConfig() 40 | { 41 | EEPROM.begin(512); 42 | 43 | char ok[3]; 44 | EEPROM.get(264, ok); 45 | 46 | if (String(ok) == String("OK")) 47 | { 48 | Serial.print("Loading Storage (512b) ... "); 49 | // Wifi part 50 | EEPROM.get(0, ssid); // 32 51 | EEPROM.get(32, password); // 32 52 | 53 | EEPROM.get(64, beep_enable); // 1 54 | EEPROM.get(65, analog_enable); // 1 55 | EEPROM.get(66, display_enable); // 1 56 | EEPROM.get(67, ads1115_enable); // 1 57 | EEPROM.get(68, mpu9250_enable); // 1 58 | EEPROM.get(69, motor_enable); // 1 59 | EEPROM.get(70, tracker_enable); // 1 60 | 61 | EEPROM.get(71, latitude); // 4 62 | EEPROM.get(75, longitude); // 4 63 | EEPROM.get(79, altitude); // 4 64 | EEPROM.get(83, declination); // 4 65 | 66 | EEPROM.get(87, ms_per_deg); // 4 67 | 68 | EEPROM.get(91, tle_name); // 32 69 | EEPROM.get(123, tle_line1); // 70 70 | EEPROM.get(193, tle_line2); // 70 71 | 72 | EEPROM.get(263, auto_control); // 1 73 | Serial.println("Done"); 74 | } 75 | else 76 | { 77 | saveConfig(); 78 | } 79 | 80 | EEPROM.end(); 81 | } 82 | 83 | void saveConfig() 84 | { 85 | EEPROM.begin(512); 86 | 87 | Serial.print("Saving Storage (512b) ... "); 88 | // Wifi part 89 | EEPROM.put(0, ssid); // 32 90 | EEPROM.put(32, password); // 32 91 | 92 | EEPROM.put(64, beep_enable); // 1 93 | EEPROM.put(65, analog_enable); // 1 94 | EEPROM.put(66, display_enable); // 1 95 | EEPROM.put(67, ads1115_enable); // 1 96 | EEPROM.put(68, mpu9250_enable); // 1 97 | EEPROM.put(69, motor_enable); // 1 98 | EEPROM.put(70, tracker_enable); // 1 99 | 100 | EEPROM.put(71, latitude); // 4 101 | EEPROM.put(75, longitude); // 4 102 | EEPROM.put(79, altitude); // 4 103 | EEPROM.put(83, declination); // 4 104 | 105 | EEPROM.put(87, ms_per_deg); // 4 106 | 107 | EEPROM.put(91, tle_name); // 32 108 | EEPROM.put(123, tle_line1); // 70 109 | EEPROM.put(193, tle_line2); // 70 110 | 111 | EEPROM.put(263, auto_control); // 1 112 | 113 | char ok[3] = "OK"; 114 | EEPROM.put(264, ok); 115 | EEPROM.commit(); 116 | Serial.println("Done"); 117 | 118 | EEPROM.end(); 119 | } 120 | -------------------------------------------------------------------------------- /eeprom.h: -------------------------------------------------------------------------------- 1 | #ifndef EEPROM_H 2 | #define EEPROM_H 3 | 4 | extern char ssid[32]; 5 | extern char password[32]; 6 | 7 | // peripheral devices 8 | extern uint8_t beep_enable; 9 | extern uint8_t analog_enable; 10 | extern uint8_t display_enable; 11 | extern uint8_t ads1115_enable; 12 | extern uint8_t mpu9250_enable; 13 | extern uint8_t motor_enable; 14 | extern uint8_t tracker_enable; 15 | 16 | extern float ms_per_deg; 17 | 18 | extern float latitude; 19 | extern float longitude; 20 | extern float altitude; 21 | extern float declination; 22 | 23 | extern char tle_name[32]; 24 | extern char tle_line1[70]; 25 | extern char tle_line2[70]; 26 | 27 | extern uint8_t auto_control; 28 | 29 | // extern uint8_t post_enable; 30 | // extern char api_key[32]; 31 | // extern char api_url[256]; 32 | // extern uint16_t http_timeout; 33 | 34 | void loadConfig(); 35 | void saveConfig(); 36 | 37 | #endif -------------------------------------------------------------------------------- /html.h: -------------------------------------------------------------------------------- 1 | #ifndef HTML_H 2 | #define HTML_H 3 | 4 | const char index_html[] PROGMEM = R"rawliteral( 5 | 6 | 7 | 8 | 9 | 10 | ESP8266 11 | 12 | 258 | 259 | 260 | 261 | 277 |
278 |
279 | 563 | 564 | 565 | 566 | )rawliteral"; 567 | 568 | #endif -------------------------------------------------------------------------------- /images/20240222_111427.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/20240222_111427.jpg -------------------------------------------------------------------------------- /images/20240222_111456.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/20240222_111456.jpg -------------------------------------------------------------------------------- /images/20240223_172119.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/20240223_172119.jpg -------------------------------------------------------------------------------- /images/20240223_172133.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/20240223_172133.jpg -------------------------------------------------------------------------------- /images/Screenshot_20240223_172631_Chrome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/Screenshot_20240223_172631_Chrome.jpg -------------------------------------------------------------------------------- /images/Screenshot_20240223_172711_Chrome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/Screenshot_20240223_172711_Chrome.jpg -------------------------------------------------------------------------------- /images/Screenshot_20240223_172840_Chrome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/Screenshot_20240223_172840_Chrome.jpg -------------------------------------------------------------------------------- /images/Screenshot_20240223_172920_Chrome.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/Screenshot_20240223_172920_Chrome.jpg -------------------------------------------------------------------------------- /images/wiring.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kerpz/ArduinoSatelliteTracker/e48587a167b609359a7c1fef8f6fd1cc3f453ac3/images/wiring.jpg -------------------------------------------------------------------------------- /mpu6050.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mpu6050.h" 5 | // Vcc = 3v3 6 | // D1 = SCL 7 | // D2 = SDA 8 | const int MPU_addr = 0x68; 9 | const int minVal = 265; 10 | const int maxVal = 402; 11 | 12 | float old_yaw; 13 | float old_pitch; 14 | float old_roll; 15 | float old_temperature; 16 | 17 | void mpu6050Setup() 18 | { 19 | Wire.begin(); 20 | Wire.beginTransmission(MPU_addr); 21 | Wire.write(0x6B); 22 | Wire.write(0); 23 | Wire.endTransmission(true); 24 | // mpu9250_found = 1; 25 | } 26 | 27 | void mpu6050Loop() 28 | { 29 | float AX, AY, AZ, TP, GX, GY, GZ; 30 | Wire.beginTransmission(MPU_addr); 31 | Wire.write(0x3B); 32 | Wire.endTransmission(false); 33 | Wire.requestFrom(MPU_addr, 14, 1); 34 | 35 | AX = Wire.read() << 8 | Wire.read(); 36 | AY = Wire.read() << 8 | Wire.read(); 37 | AZ = Wire.read() << 8 | Wire.read(); 38 | TP = Wire.read() << 8 | Wire.read(); 39 | GX = Wire.read() << 8 | Wire.read(); 40 | GY = Wire.read() << 8 | Wire.read(); 41 | GZ = Wire.read() << 8 | Wire.read(); 42 | 43 | old_temperature = TP / 340.00 + 36.53; 44 | 45 | int xAng = map(AX, minVal, maxVal, -90, 90); 46 | int yAng = map(AY, minVal, maxVal, -90, 90); 47 | int zAng = map(AZ, minVal, maxVal, -90, 90); 48 | 49 | old_yaw = RAD_TO_DEG * (atan2(-yAng, -zAng) + PI); 50 | old_pitch = RAD_TO_DEG * (atan2(-xAng, -zAng) + PI); 51 | old_roll = RAD_TO_DEG * (atan2(-yAng, -xAng) + PI); 52 | // pitch = 180 * atan2(accelX, sqrt(accelY*accelY + accelZ*accelZ))/PI; 53 | // roll = 180 * atan2(accelY, sqrt(accelX*accelX + accelZ*accelZ))/PI; 54 | // https://roboticsclubiitk.github.io/2017/12/21/Beginners-Guide-to-IMU.html 55 | // 56 | } 57 | -------------------------------------------------------------------------------- /mpu6050.h: -------------------------------------------------------------------------------- 1 | #ifndef MPU6050_H 2 | #define MPU6050_H 3 | 4 | #include "app.h" 5 | 6 | extern float old_yaw; 7 | extern float old_pitch; 8 | extern float old_roll; 9 | extern float old_temperature; 10 | 11 | void mpu6050Setup(); 12 | void mpu6050Loop(); 13 | 14 | #endif -------------------------------------------------------------------------------- /mpu9250.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mpu9250.h" 5 | // pitch = 180 * atan2(accelX, sqrt(accelY*accelY + accelZ*accelZ))/PI; 6 | // roll = 180 * atan2(accelY, sqrt(accelX*accelX + accelZ*accelZ))/PI; 7 | // https://roboticsclubiitk.github.io/2017/12/21/Beginners-Guide-to-IMU.html 8 | 9 | // https://github.com/miniben-90/mpu9250 10 | // https://github.com/psiphi75/esp-mpu9250/blob/master/components/mpu9250/calibrate.c 11 | // https://github.com/makerportal/mpu92-calibration 12 | 13 | float yaw; 14 | float pitch; 15 | float roll; 16 | float temperature; 17 | 18 | uint16_t mpu_error; 19 | 20 | // Specify sensor full scale 21 | // uint8_t Gscale = GFS_250DPS, Ascale = AFS_2G, Mscale = MFS_16BITS, Mmode = M_100Hz, sampleRate = 0x04; 22 | uint8_t Gscale = GFS_250DPS, Ascale = AFS_2G, Mscale = MFS_16BITS, Mmode = M_100Hz, sampleRate = 0x00; 23 | float aRes, gRes, mRes; // scale 24 | 25 | // vvvvvvvvvvvvvvvvvv VERY VERY IMPORTANT vvvvvvvvvvvvvvvvvvvvvvvvvvvvv 26 | // These are the previously determined offsets and correction factors for accelerometer and magnetometer, using MPU9250_cal and Magneto 1.2 27 | // The AHRS will NOT work well or at all if these are not correct 28 | // 29 | // redetermined 12/25/2020 (0 rejected) 30 | // acel offsets and correction matrix 31 | // float A_B[3] { 565.83, 195.43, 848.90 }; 32 | 33 | // float A_Ainv[3][3] { 34 | // {1.00380, -0.00143, 0.00704}, 35 | // {-0.00143, 1.00976, -0.00026}, 36 | // {0.00704, -0.00026, 0.98564} 37 | // }; 38 | 39 | // mag offsets and correction matrix 40 | // float M_B[3] {17.22, 28.11, -39.81}; 41 | 42 | // float M_Ainv[3][3] { 43 | // {1.19679, 0.00488, 0.00902}, 44 | // {0.00488, 1.20826, 0.00392}, 45 | // {0.00902, 0.00392, 1.21853} 46 | // }; 47 | 48 | float G_off[3] = {0.0, 0.0, 0.0}; // raw offsets, determined for gyro at rest 49 | float M_coeffs[3] = {0.0, 0.0, 0.0}; 50 | 51 | int16_t aRaw[3]; 52 | int16_t gRaw[3]; 53 | int16_t mRaw[3]; 54 | int16_t tRaw; 55 | 56 | float Axyz[3]; 57 | float Gxyz[3]; 58 | float Mxyz[3]; 59 | 60 | float SelfTest[6]; // holds results of gyro and accelerometer self test 61 | 62 | // These are the free parameters in the Mahony filter and fusion scheme, 63 | // Kp for proportional feedback, Ki for integral 64 | // with MPU-9250, angles start oscillating at Kp=40. Ki does not seem to help and is not required. 65 | #define Kp 30.0 66 | #define Ki 0.0 67 | 68 | // Madwick filter parameters 69 | static float GyroMeasError = 40.0 * (PI / 180.0); 70 | // gyroscope measurement drift in rad/s/s (start at 0.0 deg/s/s) 71 | static float GyroMeasDrift = 0.0 * (PI / 180.0); 72 | // There is a tradeoff in the beta parameter between accuracy and response 73 | // speed. In the original Madgwick study, beta of 0.041 (corresponding to 74 | // GyroMeasError of 2.7 degrees/s) was found to give optimal accuracy. 75 | // However, with this value, the LSM9SD0 response time is about 10 seconds 76 | // to a stable initial quaternion. By increasing beta by factor of 77 | // fifteen, the response time constant is reduced to ~2 sec. I haven't 78 | // noticed any reduction in solution accuracy. 79 | static float beta = sqrt(3.0 / 4.0) * GyroMeasError; // Compute beta 80 | // Compute zeta, the other free parameter in the Madgwick scheme usually 81 | // set to a small or zero value 82 | static float zeta = sqrt(3.0 / 4.0) * GyroMeasDrift; 83 | 84 | uint32_t now = 0, last = 0; 85 | float deltat = 0.0; 86 | 87 | float q[4] = {1.0, 0.0, 0.0, 0.0}; // vector to hold quaternion 88 | float eInt[3] = {0.0, 0.0, 0.0}; // vector to hold integral error for Mahony method 89 | 90 | void I2Cread(uint8_t Address, uint8_t Register, uint8_t Nbytes, uint8_t *Data) 91 | { 92 | Wire.beginTransmission(Address); 93 | Wire.write(Register); 94 | if (Wire.endTransmission() != 0) 95 | { 96 | mpu_error++; 97 | } 98 | else 99 | { 100 | Wire.requestFrom(Address, Nbytes); 101 | uint8_t index = 0; 102 | while (Wire.available()) 103 | Data[index++] = Wire.read(); 104 | } 105 | } 106 | 107 | void I2Cwrite(uint8_t Address, uint8_t Register, uint8_t Data) 108 | { 109 | Wire.beginTransmission(Address); 110 | Wire.write(Register); 111 | Wire.write(Data); 112 | if (Wire.endTransmission() != 0) 113 | mpu_error++; 114 | } 115 | 116 | //=================================================================================================================== 117 | //====== Set of useful function to access acceleration. gyroscope, magnetometer, and temperature data 118 | //=================================================================================================================== 119 | void getMres() 120 | { 121 | switch (Mscale) 122 | { 123 | // Possible magnetometer scales (and their register bit settings) are: 124 | // 14 bit resolution (0) and 16 bit resolution (1) 125 | case MFS_14BITS: 126 | mRes = 4912.0 / 8190.0; // Proper scale to return uT 127 | break; 128 | case MFS_16BITS: 129 | mRes = 4912.0 / 32760.0; // Proper scale to return uT 130 | break; 131 | } 132 | } 133 | 134 | void getGres() 135 | { 136 | switch (Gscale) 137 | { 138 | // Possible gyro scales (and their register bit settings) are: 139 | // 250 DPS (00), 500 DPS (01), 1000 DPS (10), and 2000 DPS (11). 140 | // Here's a bit of an algorith to calculate DPS/(ADC tick) based on that 2-bit value: 141 | case GFS_250DPS: 142 | gRes = 250.0 / 32768.0; 143 | break; 144 | case GFS_500DPS: 145 | gRes = 500.0 / 32768.0; 146 | break; 147 | case GFS_1000DPS: 148 | gRes = 1000.0 / 32768.0; 149 | break; 150 | case GFS_2000DPS: 151 | gRes = 2000.0 / 32768.0; 152 | break; 153 | } 154 | } 155 | 156 | void getAres() 157 | { 158 | switch (Ascale) 159 | { 160 | // Possible accelerometer scales (and their register bit settings) are: 161 | // 2 Gs (00), 4 Gs (01), 8 Gs (10), and 16 Gs (11). 162 | // Here's a bit of an algorith to calculate DPS/(ADC tick) based on that 2-bit value: 163 | case AFS_2G: 164 | aRes = 2.0 / 32768.0; 165 | break; 166 | case AFS_4G: 167 | aRes = 4.0 / 32768.0; 168 | break; 169 | case AFS_8G: 170 | aRes = 8.0 / 32768.0; 171 | break; 172 | case AFS_16G: 173 | aRes = 16.0 / 32768.0; 174 | break; 175 | } 176 | } 177 | 178 | void readAccelData(int16_t *destination) 179 | { 180 | uint8_t rawData[6]; // x/y/z accel register data stored here 181 | I2Cread(MPU9250_ADDRESS, ACCEL_XOUT_H, 6, &rawData[0]); // Read the six raw data registers into data array 182 | destination[0] = ((int16_t)rawData[0] << 8) | rawData[1]; // Turn the MSB and LSB into a signed 16-bit value 183 | destination[1] = ((int16_t)rawData[2] << 8) | rawData[3]; 184 | destination[2] = ((int16_t)rawData[4] << 8) | rawData[5]; 185 | } 186 | 187 | void readGyroData(int16_t *destination) 188 | { 189 | uint8_t rawData[6]; // x/y/z gyro register data stored here 190 | I2Cread(MPU9250_ADDRESS, GYRO_XOUT_H, 6, &rawData[0]); // Read the six raw data registers sequentially into data array 191 | destination[0] = ((int16_t)rawData[0] << 8) | rawData[1]; // Turn the MSB and LSB into a signed 16-bit value 192 | destination[1] = ((int16_t)rawData[2] << 8) | rawData[3]; 193 | destination[2] = ((int16_t)rawData[4] << 8) | rawData[5]; 194 | } 195 | 196 | void readMagData(int16_t *destination) 197 | { 198 | uint8_t Buf[1] = {0}; 199 | uint8_t rawData[7]; // x/y/z gyro register data, ST2 register stored here, must read ST2 at end of data acquisition 200 | 201 | // I2Cwrite(MPU9250_ADDRESS, INT_PIN_CFG, 0x02); //set i2c bypass enable pin to true to access magnetometer 202 | // delay(10); 203 | // I2Cwrite(AK8963_ADDRESS, AK8963_CNTL, 0x01); //enable the magnetometer 204 | // delay(10); 205 | 206 | // I2Cread(AK8963_ADDRESS, AK8963_ST1, 1, Buf); 207 | // if (Buf[0] & 0x01) { 208 | I2Cread(AK8963_ADDRESS, AK8963_XOUT_L, 7, &rawData[0]); // Read the six raw data and ST2 registers sequentially into data array 209 | if (!(rawData[6] & 0x08)) 210 | { // Check if magnetic sensor overflow set, if not then report data 211 | destination[0] = ((int16_t)rawData[1] << 8) | rawData[0]; // Turn the MSB and LSB into a signed 16-bit value 212 | destination[1] = ((int16_t)rawData[3] << 8) | rawData[2]; // Data stored as little Endian 213 | destination[2] = ((int16_t)rawData[5] << 8) | rawData[4]; 214 | } 215 | //} 216 | } 217 | 218 | int16_t readTempData() 219 | { 220 | uint8_t rawData[2]; // x/y/z gyro register data stored here 221 | I2Cread(MPU9250_ADDRESS, TEMP_OUT_H, 2, &rawData[0]); // Read the two raw data registers sequentially into data array 222 | return ((int16_t)rawData[0] << 8) | rawData[1]; // Turn the MSB and LSB into a 16-bit value 223 | } 224 | 225 | void mpu9250Init() 226 | { 227 | uint8_t Buf[1] = {0}; 228 | I2Cwrite(MPU9250_ADDRESS, PWR_MGMT_1, 0x00); 229 | delay(100); 230 | I2Cwrite(MPU9250_ADDRESS, PWR_MGMT_1, 0x01); 231 | delay(200); 232 | 233 | // Configure Gyro and Thermometer 234 | // Disable FSYNC and set thermometer and gyro bandwidth to 41 and 42 Hz, respectively; 235 | // minimum delay time for this setting is 5.9 ms, which means sensor fusion update rates cannot 236 | // be higher than 1 / 0.0059 = 170 Hz 237 | // DLPF_CFG = bits 2:0 = 011; this limits the sample rate to 1000 Hz for both 238 | // With the MPU9250, it is possible to get gyro sample rates of 32 kHz (!), 8 kHz, or 1 kHz 239 | I2Cwrite(MPU9250_ADDRESS, CONFIG, 0x00); // 0x03 240 | 241 | // Set sample rate = gyroscope output rate/(1 + SMPLRT_DIV) 242 | I2Cwrite(MPU9250_ADDRESS, SMPLRT_DIV, sampleRate); 243 | 244 | // Set gyroscope full scale range 245 | // Range selects FS_SEL and AFS_SEL are 0 - 3, so 2-bit values are left-shifted into positions 4:3 246 | I2Cread(MPU9250_ADDRESS, GYRO_CONFIG, 1, Buf); // get current GYRO_CONFIG register value 247 | // Buf[0] = Buf[0] & ~0xE0; // Clear self-test bits [7:5] 248 | Buf[0] = Buf[0] & ~0x02; // Clear Fchoice bits [1:0] // 0x03 249 | Buf[0] = Buf[0] & ~0x18; // Clear GFS bits [4:3] 250 | Buf[0] = Buf[0] | Gscale << 3; // Set full scale range for the gyro 251 | I2Cwrite(MPU9250_ADDRESS, GYRO_CONFIG, Buf[0]); // Write new GYRO_CONFIG value to register 252 | 253 | // Set accelerometer full-scale range configuration 254 | I2Cread(MPU9250_ADDRESS, ACCEL_CONFIG, 1, Buf); // get current ACCEL_CONFIG register value 255 | // Buf[0] = Buf[0] & ~0xE0; // Clear self-test bits [7:5] 256 | Buf[0] = Buf[0] & ~0x18; // Clear AFS bits [4:3] 257 | Buf[0] = Buf[0] | Ascale << 3; // Set full scale range for the accelerometer 258 | I2Cwrite(MPU9250_ADDRESS, ACCEL_CONFIG, Buf[0]); // Write new ACCEL_CONFIG register value 259 | 260 | // Set accelerometer sample rate configuration 261 | // It is possible to get a 4 kHz sample rate from the accelerometer by choosing 1 for 262 | // accel_fchoice_b bit [3]; in this case the bandwidth is 1.13 kHz 263 | // I2Cread(MPU9250_ADDRESS, ACCEL_CONFIG2, 1, Buf); // get current ACCEL_CONFIG2 register value 264 | // Buf[0] = Buf[0] & ~0x0F; // Clear accel_fchoice_b (bit 3) and A_DLPFG (bits [2:0]) 265 | // Buf[0] = Buf[0] | 0x03; // Set accelerometer rate to 1 kHz and bandwidth to 41 Hz 266 | // I2Cwrite(MPU9250_ADDRESS, ACCEL_CONFIG2, Buf[0]); // Write new ACCEL_CONFIG2 register value 267 | 268 | I2Cwrite(MPU9250_ADDRESS, INT_PIN_CFG, 0x22); // 0x10 269 | // enable ak8963 270 | I2Cwrite(MPU9250_ADDRESS, INT_ENABLE, 0x01); 271 | delay(100); 272 | 273 | // I2Cwrite(MPU9250_ADDRESS, USER_CTRL, 0x20); // Enable I2C Master mode 274 | // I2Cwrite(MPU9250_ADDRESS, I2C_MST_CTRL, 0x1D); // I2C configuration STOP after each transaction, master I2C bus at 400 KHz 275 | // I2Cwrite(MPU9250_ADDRESS, I2C_MST_DELAY_CTRL, 0x81); // Use blocking data retreival and enable delay for mag sample rate mismatch 276 | // I2Cwrite(MPU9250_ADDRESS, I2C_SLV4_CTRL, 0x01); // Delay mag data retrieval to on 277 | } 278 | 279 | void mpu9250Calibrate(float *dest1, float *dest2) 280 | { 281 | uint8_t data[12]; // data array to hold accelerometer and gyro x, y, z, data 282 | uint16_t ii, packet_count, fifo_count; 283 | int32_t gyro_bias[3] = {0, 0, 0}; 284 | int32_t accel_bias[3] = {0, 0, 0}; 285 | 286 | // reset device 287 | I2Cwrite(MPU9250_ADDRESS, PWR_MGMT_1, 0x80); // Write a one to bit 7 reset bit; toggle reset device 288 | delay(100); 289 | 290 | // get stable time source; Auto select clock source to be PLL gyroscope reference if ready 291 | // else use the internal oscillator, bits 2:0 = 001 292 | I2Cwrite(MPU9250_ADDRESS, PWR_MGMT_1, 0x01); 293 | I2Cwrite(MPU9250_ADDRESS, PWR_MGMT_2, 0x00); 294 | delay(200); 295 | 296 | // Configure device for bias calculation 297 | I2Cwrite(MPU9250_ADDRESS, INT_ENABLE, 0x00); // Disable all interrupts 298 | I2Cwrite(MPU9250_ADDRESS, FIFO_EN, 0x00); // Disable FIFO 299 | I2Cwrite(MPU9250_ADDRESS, PWR_MGMT_1, 0x00); // Turn on internal clock source 300 | I2Cwrite(MPU9250_ADDRESS, I2C_MST_CTRL, 0x00); // Disable I2C master 301 | I2Cwrite(MPU9250_ADDRESS, USER_CTRL, 0x00); // Disable FIFO and I2C master modes 302 | I2Cwrite(MPU9250_ADDRESS, USER_CTRL, 0x0C); // Reset FIFO and DMP 303 | delay(15); 304 | 305 | // Configure MPU6050 gyro and accelerometer for bias calculation 306 | I2Cwrite(MPU9250_ADDRESS, CONFIG, 0x01); // Set low-pass filter to 188 Hz 307 | I2Cwrite(MPU9250_ADDRESS, SMPLRT_DIV, 0x00); // Set sample rate to 1 kHz 308 | I2Cwrite(MPU9250_ADDRESS, GYRO_CONFIG, 0x00); // Set gyro full-scale to 250 degrees per second, maximum sensitivity 309 | I2Cwrite(MPU9250_ADDRESS, ACCEL_CONFIG, 0x00); // Set accelerometer full-scale to 2 g, maximum sensitivity 310 | 311 | uint16_t gyrosensitivity = 131; // = 131 LSB/degrees/sec 312 | uint16_t accelsensitivity = 16384; // = 16384 LSB/g 313 | 314 | // Configure FIFO to capture accelerometer and gyro data for bias calculation 315 | I2Cwrite(MPU9250_ADDRESS, USER_CTRL, 0x40); // Enable FIFO 316 | I2Cwrite(MPU9250_ADDRESS, FIFO_EN, 0x78); // Enable gyro and accelerometer sensors for FIFO (max size 512 bytes in MPU-9150) 317 | delay(40); // accumulate 40 samples in 40 milliseconds = 480 bytes 318 | 319 | // At end of sample accumulation, turn off FIFO sensor read 320 | I2Cwrite(MPU9250_ADDRESS, FIFO_EN, 0x00); // Disable gyro and accelerometer sensors for FIFO 321 | I2Cread(MPU9250_ADDRESS, FIFO_COUNTH, 2, &data[0]); 322 | fifo_count = ((uint16_t)data[0] << 8) | data[1]; 323 | packet_count = fifo_count / 12; // How many sets of full gyro and accelerometer data for averaging 324 | 325 | for (ii = 0; ii < packet_count; ii++) 326 | { 327 | int16_t accel_temp[3] = {0, 0, 0}, gyro_temp[3] = {0, 0, 0}; 328 | I2Cread(MPU9250_ADDRESS, FIFO_R_W, 12, &data[0]); 329 | accel_temp[0] = (int16_t)(((int16_t)data[0] << 8) | data[1]); // Form signed 16-bit integer for each sample in FIFO 330 | accel_temp[1] = (int16_t)(((int16_t)data[2] << 8) | data[3]); 331 | accel_temp[2] = (int16_t)(((int16_t)data[4] << 8) | data[5]); 332 | gyro_temp[0] = (int16_t)(((int16_t)data[6] << 8) | data[7]); 333 | gyro_temp[1] = (int16_t)(((int16_t)data[8] << 8) | data[9]); 334 | gyro_temp[2] = (int16_t)(((int16_t)data[10] << 8) | data[11]); 335 | 336 | accel_bias[0] += (int32_t)accel_temp[0]; // Sum individual signed 16-bit biases to get accumulated signed 32-bit biases 337 | accel_bias[1] += (int32_t)accel_temp[1]; 338 | accel_bias[2] += (int32_t)accel_temp[2]; 339 | gyro_bias[0] += (int32_t)gyro_temp[0]; 340 | gyro_bias[1] += (int32_t)gyro_temp[1]; 341 | gyro_bias[2] += (int32_t)gyro_temp[2]; 342 | } 343 | accel_bias[0] /= (int32_t)packet_count; // Normalize sums to get average count biases 344 | accel_bias[1] /= (int32_t)packet_count; 345 | accel_bias[2] /= (int32_t)packet_count; 346 | gyro_bias[0] /= (int32_t)packet_count; 347 | gyro_bias[1] /= (int32_t)packet_count; 348 | gyro_bias[2] /= (int32_t)packet_count; 349 | 350 | if (accel_bias[2] > 0L) 351 | { 352 | accel_bias[2] -= (int32_t)accelsensitivity; // Remove gravity from the z-axis accelerometer bias calculation 353 | } 354 | else 355 | { 356 | accel_bias[2] += (int32_t)accelsensitivity; 357 | } 358 | 359 | // Construct the gyro biases for push to the hardware gyro bias registers, which are reset to zero upon device startup 360 | data[0] = (-gyro_bias[0] / 4 >> 8) & 0xFF; // Divide by 4 to get 32.9 LSB per deg/s to conform to expected bias input format 361 | data[1] = (-gyro_bias[0] / 4) & 0xFF; // Biases are additive, so change sign on calculated average gyro biases 362 | data[2] = (-gyro_bias[1] / 4 >> 8) & 0xFF; 363 | data[3] = (-gyro_bias[1] / 4) & 0xFF; 364 | data[4] = (-gyro_bias[2] / 4 >> 8) & 0xFF; 365 | data[5] = (-gyro_bias[2] / 4) & 0xFF; 366 | 367 | // Push gyro biases to hardware registers 368 | I2Cwrite(MPU9250_ADDRESS, XG_OFFSET_H, data[0]); 369 | I2Cwrite(MPU9250_ADDRESS, XG_OFFSET_L, data[1]); 370 | I2Cwrite(MPU9250_ADDRESS, YG_OFFSET_H, data[2]); 371 | I2Cwrite(MPU9250_ADDRESS, YG_OFFSET_L, data[3]); 372 | I2Cwrite(MPU9250_ADDRESS, ZG_OFFSET_H, data[4]); 373 | I2Cwrite(MPU9250_ADDRESS, ZG_OFFSET_L, data[5]); 374 | 375 | // Output scaled gyro biases for display in the main program 376 | dest1[0] = (float)gyro_bias[0] / (float)gyrosensitivity; 377 | dest1[1] = (float)gyro_bias[1] / (float)gyrosensitivity; 378 | dest1[2] = (float)gyro_bias[2] / (float)gyrosensitivity; 379 | 380 | // Construct the accelerometer biases for push to the hardware accelerometer bias registers. These registers contain 381 | // factory trim values which must be added to the calculated accelerometer biases; on boot up these registers will hold 382 | // non-zero values. In addition, bit 0 of the lower byte must be preserved since it is used for temperature 383 | // compensation calculations. Accelerometer bias registers expect bias input as 2048 LSB per g, so that 384 | // the accelerometer biases calculated above must be divided by 8. 385 | 386 | int32_t accel_bias_reg[3] = {0, 0, 0}; // A place to hold the factory accelerometer trim biases 387 | I2Cread(MPU9250_ADDRESS, XA_OFFSET_H, 2, &data[0]); 388 | accel_bias_reg[0] = (int32_t)(((int16_t)data[0] << 8) | data[1]); 389 | I2Cread(MPU9250_ADDRESS, YA_OFFSET_H, 2, &data[0]); 390 | accel_bias_reg[1] = (int32_t)(((int16_t)data[0] << 8) | data[1]); 391 | I2Cread(MPU9250_ADDRESS, ZA_OFFSET_H, 2, &data[0]); 392 | accel_bias_reg[2] = (int32_t)(((int16_t)data[0] << 8) | data[1]); 393 | 394 | uint32_t mask = 1uL; // Define mask for temperature compensation bit 0 of lower byte of accelerometer bias registers 395 | uint8_t mask_bit[3] = {0, 0, 0}; // Define array to hold mask bit for each accelerometer bias axis 396 | 397 | for (ii = 0; ii < 3; ii++) 398 | { 399 | if ((accel_bias_reg[ii] & mask)) 400 | mask_bit[ii] = 0x01; // If temperature compensation bit is set, record that fact in mask_bit 401 | } 402 | 403 | // Construct total accelerometer bias, including calculated average accelerometer bias from above 404 | accel_bias_reg[0] -= (accel_bias[0] / 8); // Subtract calculated averaged accelerometer bias scaled to 2048 LSB/g (16 g full scale) 405 | accel_bias_reg[1] -= (accel_bias[1] / 8); 406 | accel_bias_reg[2] -= (accel_bias[2] / 8); 407 | 408 | data[0] = (accel_bias_reg[0] >> 8) & 0xFF; 409 | data[1] = (accel_bias_reg[0]) & 0xFF; 410 | data[1] = data[1] | mask_bit[0]; // preserve temperature compensation bit when writing back to accelerometer bias registers 411 | data[2] = (accel_bias_reg[1] >> 8) & 0xFF; 412 | data[3] = (accel_bias_reg[1]) & 0xFF; 413 | data[3] = data[3] | mask_bit[1]; // preserve temperature compensation bit when writing back to accelerometer bias registers 414 | data[4] = (accel_bias_reg[2] >> 8) & 0xFF; 415 | data[5] = (accel_bias_reg[2]) & 0xFF; 416 | data[5] = data[5] | mask_bit[2]; // preserve temperature compensation bit when writing back to accelerometer bias registers 417 | 418 | // Apparently this is not working for the acceleration biases in the MPU-9250 419 | // Are we handling the temperature correction bit properly? 420 | // Push accelerometer biases to hardware registers 421 | I2Cwrite(MPU9250_ADDRESS, XA_OFFSET_H, data[0]); 422 | I2Cwrite(MPU9250_ADDRESS, XA_OFFSET_L, data[1]); 423 | I2Cwrite(MPU9250_ADDRESS, YA_OFFSET_H, data[2]); 424 | I2Cwrite(MPU9250_ADDRESS, YA_OFFSET_L, data[3]); 425 | I2Cwrite(MPU9250_ADDRESS, ZA_OFFSET_H, data[4]); 426 | I2Cwrite(MPU9250_ADDRESS, ZA_OFFSET_L, data[5]); 427 | 428 | // Output scaled accelerometer biases for display in the main program 429 | dest2[0] = (float)accel_bias[0] / (float)accelsensitivity; 430 | dest2[1] = (float)accel_bias[1] / (float)accelsensitivity; 431 | dest2[2] = (float)accel_bias[2] / (float)accelsensitivity; 432 | } 433 | 434 | /* 435 | void mpu9250MagCal(float * dest1, float * dest2) 436 | { 437 | uint16_t ii = 0, sample_count = 0; 438 | int32_t mag_bias[3] = {0, 0, 0}, mag_scale[3] = {0, 0, 0}; 439 | int16_t mag_max[3] = {-32767, -32767, -32767}, mag_min[3] = {32767, 32767, 32767}, mag_temp[3] = {0, 0, 0}; 440 | 441 | Serial.println("Mag Calibration: Wave device in a figure eight until done!"); 442 | delay(4000); 443 | 444 | // shoot for ~fifteen seconds of mag data 445 | if(Mmode == 0x02) sample_count = 128; // at 8 Hz ODR, new mag data is available every 125 ms 446 | if(Mmode == 0x06) sample_count = 1500; // at 100 Hz ODR, new mag data is available every 10 ms 447 | for(ii = 0; ii < sample_count; ii++) { 448 | readMagData(mag_temp); // Read the mag data 449 | for (int jj = 0; jj < 3; jj++) { 450 | if(mag_temp[jj] > mag_max[jj]) mag_max[jj] = mag_temp[jj]; 451 | if(mag_temp[jj] < mag_min[jj]) mag_min[jj] = mag_temp[jj]; 452 | } 453 | if(Mmode == 0x02) delay(135); // at 8 Hz ODR, new mag data is available every 125 ms 454 | if(Mmode == 0x06) delay(12); // at 100 Hz ODR, new mag data is available every 10 ms 455 | } 456 | 457 | //Serial.println("mag x min/max:"); Serial.println(mag_max[0]); Serial.println(mag_min[0]); 458 | //Serial.println("mag y min/max:"); Serial.println(mag_max[1]); Serial.println(mag_min[1]); 459 | //Serial.println("mag z min/max:"); Serial.println(mag_max[2]); Serial.println(mag_min[2]); 460 | 461 | // Get hard iron correction 462 | mag_bias[0] = (mag_max[0] + mag_min[0])/2; // get average x mag bias in counts 463 | mag_bias[1] = (mag_max[1] + mag_min[1])/2; // get average y mag bias in counts 464 | mag_bias[2] = (mag_max[2] + mag_min[2])/2; // get average z mag bias in counts 465 | 466 | dest1[0] = (float) mag_bias[0] * mRes * mCal[0]; // save mag biases in G for main program 467 | dest1[1] = (float) mag_bias[1] * mRes * mCal[1]; 468 | dest1[2] = (float) mag_bias[2] * mRes * mCal[2]; 469 | 470 | // Get soft iron correction estimate 471 | mag_scale[0] = (mag_max[0] - mag_min[0])/2; // get average x axis max chord length in counts 472 | mag_scale[1] = (mag_max[1] - mag_min[1])/2; // get average y axis max chord length in counts 473 | mag_scale[2] = (mag_max[2] - mag_min[2])/2; // get average z axis max chord length in counts 474 | 475 | float avg_rad = mag_scale[0] + mag_scale[1] + mag_scale[2]; 476 | avg_rad /= 3.0; 477 | 478 | dest2[0] = avg_rad/((float)mag_scale[0]); 479 | dest2[1] = avg_rad/((float)mag_scale[1]); 480 | dest2[2] = avg_rad/((float)mag_scale[2]); 481 | 482 | Serial.println("Mag Calibration done!"); 483 | } 484 | */ 485 | 486 | // Accelerometer and gyroscope self test; check calibration wrt factory settings 487 | // Should return percent deviation from factory trim values, +/- 14 or less deviation is a pass 488 | void mpu9250SelfTest(float *destination) 489 | { 490 | uint8_t rawData[6] = {0, 0, 0, 0, 0, 0}; 491 | uint8_t selfTest[6]; 492 | int32_t gAvg[3] = {0}, aAvg[3] = {0}, aSTAvg[3] = {0}, gSTAvg[3] = {0}; 493 | float factoryTrim[6]; 494 | uint8_t FS = 0; 495 | 496 | I2Cwrite(MPU9250_ADDRESS, SMPLRT_DIV, 0x00); // Set gyro sample rate to 1 kHz 497 | I2Cwrite(MPU9250_ADDRESS, CONFIG, 0x02); // Set gyro sample rate to 1 kHz and DLPF to 92 Hz 498 | I2Cwrite(MPU9250_ADDRESS, GYRO_CONFIG, FS << 3); // Set full scale range for the gyro to 250 dps 499 | I2Cwrite(MPU9250_ADDRESS, ACCEL_CONFIG2, 0x02); // Set accelerometer rate to 1 kHz and bandwidth to 92 Hz 500 | I2Cwrite(MPU9250_ADDRESS, ACCEL_CONFIG, FS << 3); // Set full scale range for the accelerometer to 2 g 501 | 502 | for (int ii = 0; ii < 200; ii++) 503 | { // get average current values of gyro and acclerometer 504 | I2Cread(MPU9250_ADDRESS, ACCEL_XOUT_H, 6, &rawData[0]); // Read the six raw data registers into data array 505 | aAvg[0] += (int16_t)(((int16_t)rawData[0] << 8) | rawData[1]); // Turn the MSB and LSB into a signed 16-bit value 506 | aAvg[1] += (int16_t)(((int16_t)rawData[2] << 8) | rawData[3]); 507 | aAvg[2] += (int16_t)(((int16_t)rawData[4] << 8) | rawData[5]); 508 | 509 | I2Cread(MPU9250_ADDRESS, GYRO_XOUT_H, 6, &rawData[0]); // Read the six raw data registers sequentially into data array 510 | gAvg[0] += (int16_t)(((int16_t)rawData[0] << 8) | rawData[1]); // Turn the MSB and LSB into a signed 16-bit value 511 | gAvg[1] += (int16_t)(((int16_t)rawData[2] << 8) | rawData[3]); 512 | gAvg[2] += (int16_t)(((int16_t)rawData[4] << 8) | rawData[5]); 513 | } 514 | 515 | for (int ii = 0; ii < 3; ii++) 516 | { // Get average of 200 values and store as average current readings 517 | aAvg[ii] /= 200; 518 | gAvg[ii] /= 200; 519 | } 520 | 521 | // Configure the accelerometer for self-test 522 | I2Cwrite(MPU9250_ADDRESS, ACCEL_CONFIG, 0xE0); // Enable self test on all three axes and set accelerometer range to +/- 2 g 523 | I2Cwrite(MPU9250_ADDRESS, GYRO_CONFIG, 0xE0); // Enable self test on all three axes and set gyro range to +/- 250 degrees/s 524 | delay(25); // Delay a while to let the device stabilize 525 | 526 | for (int ii = 0; ii < 200; ii++) 527 | { // get average self-test values of gyro and acclerometer 528 | I2Cread(MPU9250_ADDRESS, ACCEL_XOUT_H, 6, &rawData[0]); // Read the six raw data registers into data array 529 | aSTAvg[0] += (int16_t)(((int16_t)rawData[0] << 8) | rawData[1]); // Turn the MSB and LSB into a signed 16-bit value 530 | aSTAvg[1] += (int16_t)(((int16_t)rawData[2] << 8) | rawData[3]); 531 | aSTAvg[2] += (int16_t)(((int16_t)rawData[4] << 8) | rawData[5]); 532 | 533 | I2Cread(MPU9250_ADDRESS, GYRO_XOUT_H, 6, &rawData[0]); // Read the six raw data registers sequentially into data array 534 | gSTAvg[0] += (int16_t)(((int16_t)rawData[0] << 8) | rawData[1]); // Turn the MSB and LSB into a signed 16-bit value 535 | gSTAvg[1] += (int16_t)(((int16_t)rawData[2] << 8) | rawData[3]); 536 | gSTAvg[2] += (int16_t)(((int16_t)rawData[4] << 8) | rawData[5]); 537 | } 538 | 539 | for (int ii = 0; ii < 3; ii++) 540 | { // Get average of 200 values and store as average self-test readings 541 | aSTAvg[ii] /= 200; 542 | gSTAvg[ii] /= 200; 543 | } 544 | 545 | // Configure the gyro and accelerometer for normal operation 546 | I2Cwrite(MPU9250_ADDRESS, ACCEL_CONFIG, 0x00); 547 | I2Cwrite(MPU9250_ADDRESS, GYRO_CONFIG, 0x00); 548 | delay(25); // Delay a while to let the device stabilize 549 | 550 | // Retrieve accelerometer and gyro factory Self-Test Code from USR_Reg 551 | I2Cread(MPU9250_ADDRESS, SELF_TEST_X_ACCEL, 1, &rawData[0]); 552 | selfTest[0] = rawData[0]; // X-axis accel self-test results 553 | I2Cread(MPU9250_ADDRESS, SELF_TEST_Y_ACCEL, 1, &rawData[0]); 554 | selfTest[1] = rawData[0]; // Y-axis accel self-test results 555 | I2Cread(MPU9250_ADDRESS, SELF_TEST_Z_ACCEL, 1, &rawData[0]); 556 | selfTest[2] = rawData[0]; // Z-axis accel self-test results 557 | I2Cread(MPU9250_ADDRESS, SELF_TEST_X_GYRO, 1, &rawData[0]); 558 | selfTest[3] = rawData[0]; // X-axis gyro self-test results 559 | I2Cread(MPU9250_ADDRESS, SELF_TEST_Y_GYRO, 1, &rawData[0]); 560 | selfTest[4] = rawData[0]; // Y-axis gyro self-test results 561 | I2Cread(MPU9250_ADDRESS, SELF_TEST_Z_GYRO, 1, &rawData[0]); 562 | selfTest[5] = rawData[0]; // Z-axis gyro self-test results 563 | 564 | // Retrieve factory self-test value from self-test code reads 565 | factoryTrim[0] = (float)(2620 / 1 << FS) * (pow(1.01, ((float)selfTest[0] - 1.0))); // FT[Xa] factory trim calculation 566 | factoryTrim[1] = (float)(2620 / 1 << FS) * (pow(1.01, ((float)selfTest[1] - 1.0))); // FT[Ya] factory trim calculation 567 | factoryTrim[2] = (float)(2620 / 1 << FS) * (pow(1.01, ((float)selfTest[2] - 1.0))); // FT[Za] factory trim calculation 568 | factoryTrim[3] = (float)(2620 / 1 << FS) * (pow(1.01, ((float)selfTest[3] - 1.0))); // FT[Xg] factory trim calculation 569 | factoryTrim[4] = (float)(2620 / 1 << FS) * (pow(1.01, ((float)selfTest[4] - 1.0))); // FT[Yg] factory trim calculation 570 | factoryTrim[5] = (float)(2620 / 1 << FS) * (pow(1.01, ((float)selfTest[5] - 1.0))); // FT[Zg] factory trim calculation 571 | 572 | // Report results as a ratio of (STR - FT)/FT; the change from Factory Trim of the Self-Test Response 573 | // To get percent, must multiply by 100 574 | for (int i = 0; i < 3; i++) 575 | { 576 | destination[i] = 100.0 * ((float)(aSTAvg[i] - aAvg[i])) / factoryTrim[i] - 100.; // Report percent differences 577 | destination[i + 3] = 100.0 * ((float)(gSTAvg[i] - gAvg[i])) / factoryTrim[i + 3] - 100.; // Report percent differences 578 | } 579 | } 580 | 581 | void ak8963Init(float *destination) 582 | { 583 | // First extract the factory calibration for each magnetometer axis 584 | uint8_t rawData[3]; // x/y/z gyro calibration data stored here 585 | I2Cwrite(AK8963_ADDRESS, AK8963_CNTL, 0x00); // Power down magnetometer 586 | delay(10); 587 | I2Cwrite(AK8963_ADDRESS, AK8963_CNTL, 0x0F); // Enter Fuse ROM access mode 588 | delay(10); 589 | I2Cread(AK8963_ADDRESS, AK8963_ASAX, 3, &rawData[0]); // Read the x-, y-, and z-axis calibration values 590 | destination[0] = ((float)rawData[0] - 128) / 256.0 + 1.0; // Return x-axis sensitivity adjustment values, etc. 591 | destination[1] = ((float)rawData[1] - 128) / 256.0 + 1.0; 592 | destination[2] = ((float)rawData[2] - 128) / 256.0 + 1.0; 593 | I2Cwrite(AK8963_ADDRESS, AK8963_CNTL, 0x00); // Power down magnetometer 594 | delay(10); 595 | // Configure the magnetometer for continuous read and highest resolution 596 | // set Mscale bit 4 to 1 (0) to enable 16 (14) bit resolution in CNTL register, 597 | // and enable continuous mode data acquisition Mmode (bits [3:0]), 0010 for 8 Hz and 0110 for 100 Hz sample rates 598 | I2Cwrite(AK8963_ADDRESS, AK8963_CNTL, Mscale << 4 | Mmode); // Set magnetometer data resolution and sample ODR 599 | delay(10); 600 | } 601 | 602 | // ------------------------------------------------------------------------------------------------------------------------------------- 603 | 604 | void MadgwickQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz, float deltat) 605 | { 606 | float q1 = q[0], q2 = q[1], q3 = q[2], q4 = q[3]; // short name local variable for readability 607 | float norm; 608 | float hx, hy, _2bx, _2bz; 609 | float s1, s2, s3, s4; 610 | float qDot1, qDot2, qDot3, qDot4; 611 | 612 | // Auxiliary variables to avoid repeated arithmetic 613 | float _2q1mx; 614 | float _2q1my; 615 | float _2q1mz; 616 | float _2q2mx; 617 | float _4bx; 618 | float _4bz; 619 | float _2q1 = 2.0f * q1; 620 | float _2q2 = 2.0f * q2; 621 | float _2q3 = 2.0f * q3; 622 | float _2q4 = 2.0f * q4; 623 | float _2q1q3 = 2.0f * q1 * q3; 624 | float _2q3q4 = 2.0f * q3 * q4; 625 | float q1q1 = q1 * q1; 626 | float q1q2 = q1 * q2; 627 | float q1q3 = q1 * q3; 628 | float q1q4 = q1 * q4; 629 | float q2q2 = q2 * q2; 630 | float q2q3 = q2 * q3; 631 | float q2q4 = q2 * q4; 632 | float q3q3 = q3 * q3; 633 | float q3q4 = q3 * q4; 634 | float q4q4 = q4 * q4; 635 | 636 | /* changed for magneto I guess 637 | // Normalise accelerometer measurement 638 | norm = sqrt(ax * ax + ay * ay + az * az); 639 | if (norm == 0.0f) return; // handle NaN 640 | norm = 1.0f / norm; 641 | ax *= norm; 642 | ay *= norm; 643 | az *= norm; 644 | 645 | // Normalise magnetometer measurement 646 | norm = sqrt(mx * mx + my * my + mz * mz); 647 | if (norm == 0.0f) return; // handle NaN 648 | norm = 1.0f / norm; 649 | mx *= norm; 650 | my *= norm; 651 | mz *= norm; 652 | */ 653 | 654 | // Reference direction of Earth's magnetic field 655 | _2q1mx = 2.0f * q1 * mx; 656 | _2q1my = 2.0f * q1 * my; 657 | _2q1mz = 2.0f * q1 * mz; 658 | _2q2mx = 2.0f * q2 * mx; 659 | hx = mx * q1q1 - _2q1my * q4 + _2q1mz * q3 + mx * q2q2 + _2q2 * my * q3 + _2q2 * mz * q4 - mx * q3q3 - mx * q4q4; 660 | hy = _2q1mx * q4 + my * q1q1 - _2q1mz * q2 + _2q2mx * q3 - my * q2q2 + my * q3q3 + _2q3 * mz * q4 - my * q4q4; 661 | _2bx = sqrt(hx * hx + hy * hy); 662 | _2bz = -_2q1mx * q3 + _2q1my * q2 + mz * q1q1 + _2q2mx * q4 - mz * q2q2 + _2q3 * my * q4 - mz * q3q3 + mz * q4q4; 663 | _4bx = 2.0f * _2bx; 664 | _4bz = 2.0f * _2bz; 665 | 666 | // Gradient decent algorithm corrective step 667 | s1 = -_2q3 * (2.0f * q2q4 - _2q1q3 - ax) + _2q2 * (2.0f * q1q2 + _2q3q4 - ay) - _2bz * q3 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q4 + _2bz * q2) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q3 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); 668 | s2 = _2q4 * (2.0f * q2q4 - _2q1q3 - ax) + _2q1 * (2.0f * q1q2 + _2q3q4 - ay) - 4.0f * q2 * (1.0f - 2.0f * q2q2 - 2.0f * q3q3 - az) + _2bz * q4 * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q3 + _2bz * q1) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q4 - _4bz * q2) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); 669 | s3 = -_2q1 * (2.0f * q2q4 - _2q1q3 - ax) + _2q4 * (2.0f * q1q2 + _2q3q4 - ay) - 4.0f * q3 * (1.0f - 2.0f * q2q2 - 2.0f * q3q3 - az) + (-_4bx * q3 - _2bz * q1) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (_2bx * q2 + _2bz * q4) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + (_2bx * q1 - _4bz * q3) * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); 670 | s4 = _2q2 * (2.0f * q2q4 - _2q1q3 - ax) + _2q3 * (2.0f * q1q2 + _2q3q4 - ay) + (-_4bx * q4 + _2bz * q2) * (_2bx * (0.5f - q3q3 - q4q4) + _2bz * (q2q4 - q1q3) - mx) + (-_2bx * q1 + _2bz * q3) * (_2bx * (q2q3 - q1q4) + _2bz * (q1q2 + q3q4) - my) + _2bx * q2 * (_2bx * (q1q3 + q2q4) + _2bz * (0.5f - q2q2 - q3q3) - mz); 671 | norm = sqrt(s1 * s1 + s2 * s2 + s3 * s3 + s4 * s4); // normalise step magnitude 672 | norm = 1.0f / norm; 673 | s1 *= norm; 674 | s2 *= norm; 675 | s3 *= norm; 676 | s4 *= norm; 677 | 678 | // Compute rate of change of quaternion 679 | qDot1 = 0.5f * (-q2 * gx - q3 * gy - q4 * gz) - beta * s1; 680 | qDot2 = 0.5f * (q1 * gx + q3 * gz - q4 * gy) - beta * s2; 681 | qDot3 = 0.5f * (q1 * gy - q2 * gz + q4 * gx) - beta * s3; 682 | qDot4 = 0.5f * (q1 * gz + q2 * gy - q3 * gx) - beta * s4; 683 | 684 | // Integrate to yield quaternion 685 | q1 += qDot1 * deltat; 686 | q2 += qDot2 * deltat; 687 | q3 += qDot3 * deltat; 688 | q4 += qDot4 * deltat; 689 | norm = sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); // normalise quaternion 690 | norm = 1.0f / norm; 691 | q[0] = q1 * norm; 692 | q[1] = q2 * norm; 693 | q[2] = q3 * norm; 694 | q[3] = q4 * norm; 695 | } 696 | // Similar to Madgwick scheme but uses proportional and integral filtering on the error between estimated reference vectors and 697 | // measured ones. 698 | void MahonyQuaternionUpdate(float ax, float ay, float az, float gx, float gy, float gz, float mx, float my, float mz, float deltat) 699 | { 700 | float q1 = q[0], q2 = q[1], q3 = q[2], q4 = q[3]; // short name local variable for readability 701 | float norm; 702 | float hx, hy, bx, bz; 703 | float vx, vy, vz, wx, wy, wz; 704 | float ex, ey, ez; 705 | float pa, pb, pc; 706 | 707 | // Auxiliary variables to avoid repeated arithmetic 708 | float q1q1 = q1 * q1; 709 | float q1q2 = q1 * q2; 710 | float q1q3 = q1 * q3; 711 | float q1q4 = q1 * q4; 712 | float q2q2 = q2 * q2; 713 | float q2q3 = q2 * q3; 714 | float q2q4 = q2 * q4; 715 | float q3q3 = q3 * q3; 716 | float q3q4 = q3 * q4; 717 | float q4q4 = q4 * q4; 718 | 719 | // Normalise accelerometer measurement 720 | norm = sqrt(ax * ax + ay * ay + az * az); 721 | if (norm == 0.0f) 722 | return; // handle NaN 723 | norm = 1.0f / norm; // use reciprocal for division 724 | ax *= norm; 725 | ay *= norm; 726 | az *= norm; 727 | 728 | // Normalise magnetometer measurement 729 | norm = sqrt(mx * mx + my * my + mz * mz); 730 | if (norm == 0.0f) 731 | return; // handle NaN 732 | norm = 1.0f / norm; // use reciprocal for division 733 | mx *= norm; 734 | my *= norm; 735 | mz *= norm; 736 | 737 | // Reference direction of Earth's magnetic field 738 | hx = 2.0f * mx * (0.5f - q3q3 - q4q4) + 2.0f * my * (q2q3 - q1q4) + 2.0f * mz * (q2q4 + q1q3); 739 | hy = 2.0f * mx * (q2q3 + q1q4) + 2.0f * my * (0.5f - q2q2 - q4q4) + 2.0f * mz * (q3q4 - q1q2); 740 | bx = sqrt((hx * hx) + (hy * hy)); 741 | bz = 2.0f * mx * (q2q4 - q1q3) + 2.0f * my * (q3q4 + q1q2) + 2.0f * mz * (0.5f - q2q2 - q3q3); 742 | 743 | // Estimated direction of gravity and magnetic field 744 | vx = 2.0f * (q2q4 - q1q3); 745 | vy = 2.0f * (q1q2 + q3q4); 746 | vz = q1q1 - q2q2 - q3q3 + q4q4; 747 | wx = 2.0f * bx * (0.5f - q3q3 - q4q4) + 2.0f * bz * (q2q4 - q1q3); 748 | wy = 2.0f * bx * (q2q3 - q1q4) + 2.0f * bz * (q1q2 + q3q4); 749 | wz = 2.0f * bx * (q1q3 + q2q4) + 2.0f * bz * (0.5f - q2q2 - q3q3); 750 | 751 | // Error is cross product between estimated direction and measured direction of gravity 752 | ex = (ay * vz - az * vy) + (my * wz - mz * wy); 753 | ey = (az * vx - ax * vz) + (mz * wx - mx * wz); 754 | ez = (ax * vy - ay * vx) + (mx * wy - my * wx); 755 | if (Ki > 0.0f) 756 | { 757 | eInt[0] += ex; // accumulate integral error 758 | eInt[1] += ey; 759 | eInt[2] += ez; 760 | } 761 | else 762 | { 763 | eInt[0] = 0.0f; // prevent integral wind up 764 | eInt[1] = 0.0f; 765 | eInt[2] = 0.0f; 766 | } 767 | 768 | // Apply feedback terms 769 | gx = gx + Kp * ex + Ki * eInt[0]; 770 | gy = gy + Kp * ey + Ki * eInt[1]; 771 | gz = gz + Kp * ez + Ki * eInt[2]; 772 | 773 | // Integrate rate of change of quaternion 774 | pa = q2; 775 | pb = q3; 776 | pc = q4; 777 | q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltat); 778 | q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * deltat); 779 | q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * deltat); 780 | q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * deltat); 781 | 782 | // Normalise quaternion 783 | norm = sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); 784 | norm = 1.0f / norm; 785 | q[0] = q1 * norm; 786 | q[1] = q2 * norm; 787 | q[2] = q3 * norm; 788 | q[3] = q4 * norm; 789 | } 790 | 791 | void MahonyQuaternionUpdate2(float ax, float ay, float az, float gx, float gy, float gz) 792 | { 793 | float norm; 794 | float vx, vy, vz; 795 | float ex, ey, ez; 796 | float qa, qb, qc; 797 | 798 | // compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) 799 | if (!((ax == 0.0) && (ay == 0.0) && (az == 0.0))) 800 | { 801 | // normalise accelerometer measurement 802 | norm = sqrt(ax * ax + ay * ay + az * az); 803 | ax /= norm; 804 | ay /= norm; 805 | az /= norm; 806 | 807 | // Estimated direction of gravity and vector perpendicular to magnetic flux 808 | vx = q[1] * q[3] - q[0] * q[2]; 809 | vy = q[0] * q[1] + q[2] * q[3]; 810 | vz = q[0] * q[0] - 0.5 + q[3] * q[3]; 811 | 812 | // Error is sum of cross product between estimated and measured direction of gravity 813 | ex = (ay * vz - az * vy); 814 | ey = (az * vx - ax * vz); 815 | ez = (ax * vy - ay * vx); 816 | 817 | // Compute and apply integral feedback if enabled 818 | if (Ki > 0.0f) 819 | { 820 | eInt[0] += Ki * ex * (1.0 / deltat); // integral error scaled by Ki 821 | eInt[1] += Ki * ey * (1.0 / deltat); 822 | eInt[2] += Ki * ez * (1.0 / deltat); 823 | gx += eInt[0]; // apply integral feedback 824 | gy += eInt[1]; 825 | gz += eInt[2]; 826 | } 827 | else 828 | { 829 | eInt[0] = 0.0; // prevent integral windup 830 | eInt[1] = 0.0; 831 | eInt[2] = 0.0; 832 | } 833 | 834 | // Apply proportional feedback 835 | gx += Kp * ex; 836 | gy += Kp * ey; 837 | gz += Kp * ez; 838 | } 839 | 840 | // Integrate rate of change of quaternion 841 | gx *= (0.5 * (1.0 / deltat)); // pre-multiply common factors 842 | gy *= (0.5 * (1.0 / deltat)); 843 | gz *= (0.5 * (1.0 / deltat)); 844 | qa = q[0]; 845 | qb = q[1]; 846 | qc = q[2]; 847 | q[0] += (-qb * gx - qc * gy - q[3] * gz); 848 | q[1] += (qa * gx + qc * gz - q[3] * gy); 849 | q[2] += (qa * gy - qb * gz + q[3] * gx); 850 | q[3] += (qa * gz + qb * gy - qc * gx); 851 | 852 | // Normalise quaternion 853 | norm = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); 854 | q[0] /= norm; 855 | q[1] /= norm; 856 | q[2] /= norm; 857 | q[3] /= norm; 858 | } 859 | 860 | void magnetoSamples(int samples) 861 | { 862 | Serial.println("Gyro bias collection ... KEEP SENSOR STILL"); 863 | for (int i = 0; i < samples; i++) 864 | { // 300 samples 865 | readGyroData(gRaw); 866 | Gxyz[0] += (float)gRaw[0]; // 250 LSB(d/s) default to radians/s 867 | Gxyz[1] += (float)gRaw[1]; 868 | Gxyz[2] += (float)gRaw[2]; 869 | } 870 | G_off[0] = Gxyz[0] / samples; 871 | G_off[1] = Gxyz[1] / samples; 872 | G_off[2] = Gxyz[2] / samples; 873 | Serial.print("Done. Gyro offsets (raw) "); 874 | Serial.print(G_off[0], 1); 875 | Serial.print(", "); 876 | Serial.print(G_off[1], 1); 877 | Serial.print(", "); 878 | Serial.print(G_off[2], 1); 879 | Serial.println(); 880 | 881 | Serial.print("Collecting "); 882 | Serial.print(samples); 883 | Serial.println(" points for scaling, 3/second"); 884 | Serial.println("TURN SENSOR VERY SLOWLY AND CAREFULLY IN 3D"); 885 | 886 | float M_mag = 0, A_mag = 0; 887 | int i, j; 888 | j = samples; 889 | while (j-- >= 0) 890 | { 891 | readAccelData(aRaw); 892 | Axyz[0] = (float)aRaw[0]; 893 | Axyz[1] = (float)aRaw[1]; 894 | Axyz[2] = (float)aRaw[2]; 895 | readMagData(mRaw); 896 | Mxyz[0] = (float)mRaw[0]; 897 | Mxyz[1] = (float)mRaw[1]; 898 | Mxyz[2] = (float)mRaw[2]; 899 | 900 | for (i = 0; i < 3; i++) 901 | { 902 | M_mag += Mxyz[i] * Mxyz[i]; 903 | A_mag += Axyz[i] * Axyz[i]; 904 | } 905 | Serial.print(aRaw[0]); 906 | Serial.print(","); 907 | Serial.print(aRaw[1]); 908 | Serial.print(","); 909 | Serial.print(aRaw[2]); 910 | Serial.print(","); 911 | Serial.print(mRaw[0]); 912 | Serial.print(","); 913 | Serial.print(mRaw[1]); 914 | Serial.print(","); 915 | Serial.println(mRaw[2]); 916 | delay(300); 917 | } 918 | Serial.print("Done. "); 919 | Serial.print("rms A = "); 920 | Serial.print(sqrt(A_mag / samples)); 921 | Serial.print(", rms M = "); 922 | Serial.println(sqrt(M_mag / samples)); 923 | } 924 | 925 | void calibrateGyro(int samples) 926 | { 927 | Serial.println("Gyro bias collection ... KEEP SENSOR STILL"); 928 | for (int i = 0; i < samples; i++) 929 | { // 500 samples 930 | readGyroData(gRaw); 931 | Gxyz[0] += (float)gRaw[0]; // 250 LSB(d/s) default to radians/s 932 | Gxyz[1] += (float)gRaw[1]; 933 | Gxyz[2] += (float)gRaw[2]; 934 | } 935 | G_off[0] = Gxyz[0] / samples; 936 | G_off[1] = Gxyz[1] / samples; 937 | G_off[2] = Gxyz[2] / samples; 938 | Serial.print("Done. Gyro offsets (raw) "); 939 | Serial.print(G_off[0], 1); 940 | Serial.print(", "); 941 | Serial.print(G_off[1], 1); 942 | Serial.print(", "); 943 | Serial.print(G_off[2], 1); 944 | Serial.println(); 945 | } 946 | 947 | void mpu9250Setup() 948 | { 949 | uint8_t Buf[1] = {0}; 950 | 951 | Serial.print("Starting MPU9250 ... "); 952 | Wire.begin(); 953 | I2Cread(MPU9250_ADDRESS, WHO_AM_I_MPU9250, 1, Buf); 954 | if (Buf[0] == 0x71) 955 | { 956 | // mpu9250_found = 1; 957 | 958 | // set conversion res 959 | getAres(); 960 | getGres(); 961 | getMres(); 962 | 963 | Serial.println("Success"); 964 | 965 | I2Cwrite(MPU9250_ADDRESS, PWR_MGMT_1, 0x80); // Set bit 7 to reset MPU9250 966 | delay(100); 967 | 968 | // mpu9250SelfTest(SelfTest); // Start by performing self test and reporting values 969 | // Serial.print("x-axis self test: acceleration trim within : "); Serial.print(SelfTest[0], 1); Serial.println("% of factory value"); 970 | // Serial.print("y-axis self test: acceleration trim within : "); Serial.print(SelfTest[1], 1); Serial.println("% of factory value"); 971 | // Serial.print("z-axis self test: acceleration trim within : "); Serial.print(SelfTest[2], 1); Serial.println("% of factory value"); 972 | // Serial.print("x-axis self test: gyration trim within : "); Serial.print(SelfTest[3], 1); Serial.println("% of factory value"); 973 | // Serial.print("y-axis self test: gyration trim within : "); Serial.print(SelfTest[4], 1); Serial.println("% of factory value"); 974 | // Serial.print("z-axis self test: gyration trim within : "); Serial.print(SelfTest[5], 1); Serial.println("% of factory value"); 975 | 976 | // Comment out if using pre-measured, pre-stored offset biases 977 | // mpu9250Calibrate(gBias, aBias); // Calibrate gyro and accelerometers, load biases in bias registers 978 | // Serial.println("accel biases (mg)"); Serial.println(1000.0 * aBias[0]); Serial.println(1000.0 * aBias[1]); Serial.println(1000.0 * aBias[2]); 979 | // Serial.println("gyro biases (dps)"); Serial.println(gBias[0]); Serial.println(gBias[1]); Serial.println(gBias[2]); 980 | // delay(1000); 981 | 982 | mpu9250Init(); 983 | // Serial.println("MPU9250 initialized for active data mode...."); // Initialize device for active mode read of acclerometer, gyroscope, and temperature 984 | 985 | Serial.print("Starting AK8963 ... "); 986 | I2Cread(AK8963_ADDRESS, WHO_AM_I_AK8963, 1, Buf); 987 | if (Buf[0] == 0x48) 988 | { 989 | // ak8963_found = 1; 990 | Serial.println("Success"); 991 | delay(10); 992 | ak8963Init(M_coeffs); 993 | 994 | // Serial.println("AK8963 initialized for active data mode...."); // Initialize device for active mode read of magnetometer 995 | // I2Cwrite(AK8963_ADDRESS, AK8963_CNTL, 0x01); //enable the magnetometer 996 | 997 | // magnetoSamples(300); 998 | // Comment out if using pre-measured, pre-stored offset biases 999 | // mpu9250MagCal(mBias, mScale); 1000 | // Serial.println("AK8963 mag biases (mG)"); Serial.println(mBias[0]); Serial.println(mBias[1]); Serial.println(mBias[2]); 1001 | // Serial.println("AK8963 mag scale (mG)"); Serial.println(mScale[0]); Serial.println(mScale[1]); Serial.println(mScale[2]); 1002 | // delay(2000); // add delay to see results before serial spew of data 1003 | 1004 | // calibrateGyro(500); 1005 | 1006 | // Serial.println("Calibration values: "); 1007 | // Serial.print("X-Axis sensitivity adjustment value "); Serial.println(mCal[0], 2); 1008 | // Serial.print("Y-Axis sensitivity adjustment value "); Serial.println(mCal[1], 2); 1009 | // Serial.print("Z-Axis sensitivity adjustment value "); Serial.println(mCal[2], 2); 1010 | } 1011 | else 1012 | { 1013 | Serial.println("Failed!"); 1014 | } 1015 | } 1016 | else 1017 | { 1018 | Serial.println("Failed!"); 1019 | } 1020 | 1021 | // I2Cwrite(MPU9250_ADDRESS, 28, ACC_FULL_SCALE_16_G); 1022 | // I2Cwrite(MPU9250_ADDRESS, 27, GYRO_FULL_SCALE_2000_DPS); 1023 | 1024 | // I2Cwrite(MPU9250_ADDRESS, 0x37, 0x02); 1025 | // I2Cwrite(MPU9250_ADDRESS, 0x0A, 0x01); // single measurement? 1026 | now = micros(); 1027 | } 1028 | 1029 | void mpu9250Loop() 1030 | { 1031 | // float temp[3]; 1032 | 1033 | uint8_t Buf[14] = {0}; 1034 | I2Cread(MPU9250_ADDRESS, INT_STATUS, 1, Buf); 1035 | if (Buf[0] & 0x01) 1036 | { 1037 | readAccelData(aRaw); 1038 | Axyz[0] = (float)aRaw[0] * aRes; 1039 | Axyz[1] = (float)aRaw[1] * aRes; 1040 | Axyz[2] = (float)aRaw[2] * aRes; 1041 | // apply offsets and scale factors from Magneto 1042 | // for (int i = 0; i < 3; i++) Axyz[i] = (Axyz[i] - A_cal[i]) * A_cal[i + 3]; 1043 | // vector_normalize(Axyz); 1044 | // for (int i = 0; i < 3; i++) temp[i] = (Axyz[i] - A_B[i]); 1045 | // Axyz[0] = A_Ainv[0][0] * temp[0] + A_Ainv[0][1] * temp[1] + A_Ainv[0][2] * temp[2]; 1046 | // Axyz[1] = A_Ainv[1][0] * temp[0] + A_Ainv[1][1] * temp[1] + A_Ainv[1][2] * temp[2]; 1047 | // Axyz[2] = A_Ainv[2][0] * temp[0] + A_Ainv[2][1] * temp[1] + A_Ainv[2][2] * temp[2]; 1048 | // vector_normalize(Axyz); 1049 | 1050 | readGyroData(gRaw); 1051 | Gxyz[0] = (float)gRaw[0] * gRes; 1052 | Gxyz[1] = (float)gRaw[1] * gRes; 1053 | Gxyz[2] = (float)gRaw[2] * gRes; 1054 | 1055 | readMagData(mRaw); 1056 | // Mxyz[0] = M_coeffs[0] * ((float) mRaw[0] / pow(2.0, 15.0)) * 4800.0; // magnetometer sensitivity: 4800 uT 1057 | // Mxyz[1] = M_coeffs[1] * ((float) mRaw[1] / pow(2.0, 15.0)) * 4800.0; 1058 | // Mxyz[2] = M_coeffs[2] * ((float) mRaw[2] / pow(2.0, 15.0)) * 4800.0; 1059 | Mxyz[0] = (float)mRaw[0] * mRes * M_coeffs[0]; 1060 | Mxyz[1] = (float)mRaw[1] * mRes * M_coeffs[1]; 1061 | Mxyz[2] = (float)mRaw[2] * mRes * M_coeffs[2]; 1062 | // apply offsets and scale factors from Magneto 1063 | // for (int i = 0; i < 3; i++) Mxyz[i] = (Mxyz[i] - M_cal[i]) * M_cal[i + 3]; 1064 | // vector_normalize(Mxyz); 1065 | // for (int i = 0; i < 3; i++) temp[i] = (Mxyz[i] - M_B[i]); 1066 | // Mxyz[0] = M_Ainv[0][0] * temp[0] + M_Ainv[0][1] * temp[1] + M_Ainv[0][2] * temp[2]; 1067 | // Mxyz[1] = M_Ainv[1][0] * temp[0] + M_Ainv[1][1] * temp[1] + M_Ainv[1][2] * temp[2]; 1068 | // Mxyz[2] = M_Ainv[2][0] * temp[0] + M_Ainv[2][1] * temp[1] + M_Ainv[2][2] * temp[2]; 1069 | // vector_normalize(Mxyz); 1070 | 1071 | tRaw = readTempData(); 1072 | temperature = ((float)tRaw) / 333.87 + 21.0; // Temperature in degrees Centigrade 1073 | 1074 | // test 1075 | /* 1076 | float bh = sqrt(Mxyz[0] * Mxyz[0] + Mxyz[1] * Mxyz[1]); 1077 | Serial.println("------------------------------------------------------------"); 1078 | Serial.println("_____________________________"); 1079 | Serial.println("ACCELEROMETER"); 1080 | Serial.print("x-dir: "); 1081 | Serial.print(Axyz[0], 2); 1082 | Serial.println(" g"); 1083 | Serial.print("y-dir: "); 1084 | Serial.print(Axyz[1], 2); 1085 | Serial.println(" g"); 1086 | Serial.print("z-dir: "); 1087 | Serial.print(Axyz[2], 2); 1088 | Serial.println(" g"); 1089 | Serial.println("_____________________________"); 1090 | Serial.println("GYROSCOPE"); 1091 | Serial.print("x-dir: "); 1092 | Serial.print(Gxyz[0], 2); 1093 | Serial.println(" dps"); 1094 | Serial.print("y-dir: "); 1095 | Serial.print(Gxyz[1], 2); 1096 | Serial.println(" dps"); 1097 | Serial.print("z-dir: "); 1098 | Serial.print(Gxyz[2], 2); 1099 | Serial.println(" dps"); 1100 | Serial.println("_____________________________"); 1101 | Serial.println("MAGNETOMETER"); 1102 | Serial.print("x-dir: "); 1103 | Serial.print(Mxyz[0], 2); 1104 | Serial.println(" uT"); 1105 | Serial.print("y-dir: "); 1106 | Serial.print(Mxyz[1], 2); 1107 | Serial.println(" uT"); 1108 | Serial.print("z-dir: "); 1109 | Serial.print(Mxyz[2], 2); 1110 | Serial.println(" uT"); 1111 | Serial.print("bh: "); 1112 | Serial.print(bh, 2); 1113 | Serial.println(" uT"); 1114 | */ 1115 | } 1116 | 1117 | now = micros(); 1118 | deltat = ((now - last) / 1000000.0f); // set integration time by time elapsed since last filter update 1119 | last = now; 1120 | 1121 | // https://github.com/TKJElectronics/KalmanFilter/blob/master/examples/MPU6050/MPU6050.ino 1122 | // #ifdef RESTRICT_PITCH // Eq. 25 and 26 1123 | // roll = atan2(aRaw[1], aRaw[2]) * RAD_TO_DEG; 1124 | // pitch = atan(-aRaw[0] / sqrt(aRaw[1] * aRaw[1] + aRaw[2] * aRaw[2])) * RAD_TO_DEG; 1125 | roll = atan2(Axyz[1], Axyz[2]); 1126 | pitch = atan(-Axyz[0] / sqrt(Axyz[1] * Axyz[1] + Axyz[2] * Axyz[2])); 1127 | // #else // Eq. 28 and 29 1128 | // roll = atan(aRaw[1] / sqrt(aRaw[0] * aRaw[0] + aRaw[2] * aRaw[2])) * RAD_TO_DEG; 1129 | // pitch = atan2(-aRaw[0], aRaw[2]) * RAD_TO_DEG; 1130 | // #endif 1131 | 1132 | // MahonyQuaternionUpdate2(Axyz[0], Axyz[1], Axyz[2], Gxyz[0], Gxyz[1], Gxyz[2]); 1133 | // yaw = -atan2(2.0f * (q[1] * q[2] + q[0] * q[3]), q[0] * q[0] + q[1] * q[1] - q[2] * q[2] - q[3] * q[3]) * 57.29577951; 1134 | // pitch = asin(2.0f * (q[1] * q[3] - q[0] * q[2])) * 57.29577951; 1135 | // roll = atan2(2.0f * (q[0] * q[1] + q[2] * q[3]), q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]) * 57.29577951; 1136 | 1137 | /* 1138 | MahonyQuaternionUpdate(Axyz[0], Axyz[1], Axyz[2], Gxyz[0], Gxyz[1], Gxyz[2], Mxyz[1], Mxyz[0], -Mxyz[2], deltat); 1139 | //MadgwickQuaternionUpdate(AX, AY, AZ, GX*PI/180.0f, GY*PI/180.0f, GZ*PI/180.0f, MY, MX, MZ); 1140 | //MahonyQuaternionUpdate(AX, AY, AZ, GX*PI/180.0f, GY*PI/180.0f, GZ*PI/180.0f, MY, MX, MZ); 1141 | 1142 | 1143 | // data proc 1144 | float qw = q[0], qx = q[1], qy = q[2], qz = q[3]; 1145 | 1146 | roll = atan2(2.0 * (qw * qx + qy * qz), 1.0 - 2.0 * (qx * qx + qy * qy)); 1147 | pitch = asin(2.0 * (qw * qy - qx * qz)); 1148 | yaw = atan2(2.0 * (qx * qy + qw * qz), 1.0 - 2.0 * ( qy * qy + qz * qz)); 1149 | // to degrees 1150 | yaw *= 180.0 / PI; 1151 | pitch *= 180.0 / PI; 1152 | roll *= 180.0 / PI; 1153 | 1154 | // http://www.ngdc.noaa.gov/geomag-web/#declination 1155 | //conventional nav, yaw increases CW from North, corrected for my declination 1156 | 1157 | yaw = -yaw + declination; 1158 | */ 1159 | } 1160 | -------------------------------------------------------------------------------- /mpu9250.h: -------------------------------------------------------------------------------- 1 | #ifndef MPU9250_H 2 | #define MPU9250_H 3 | 4 | #include "app.h" 5 | 6 | // D1 = SCL 7 | // D2 = SDA 8 | #define AK8963_ADDRESS 0x0C 9 | #define WHO_AM_I_AK8963 0x00 // should return 0x48 10 | #define INFO 0x01 11 | #define AK8963_ST1 0x02 // data ready status bit 0 12 | #define AK8963_XOUT_L 0x03 // data 13 | #define AK8963_XOUT_H 0x04 14 | #define AK8963_YOUT_L 0x05 15 | #define AK8963_YOUT_H 0x06 16 | #define AK8963_ZOUT_L 0x07 17 | #define AK8963_ZOUT_H 0x08 18 | #define AK8963_ST2 0x09 // Data overflow bit 3 and data read error status bit 2 19 | #define AK8963_CNTL 0x0A // Power down (0000), single-measurement (0001), self-test (1000) and Fuse ROM (1111) modes on bits 3:0 20 | #define AK8963_ASTC 0x0C // Self test control 21 | #define AK8963_I2CDIS 0x0F // I2C disable 22 | #define AK8963_ASAX 0x10 // Fuse ROM x-axis sensitivity adjustment value 23 | #define AK8963_ASAY 0x11 // Fuse ROM y-axis sensitivity adjustment value 24 | #define AK8963_ASAZ 0x12 // Fuse ROM z-axis sensitivity adjustment value 25 | 26 | #define MPU9250_ADDRESS 0x68 // Device address when ADO = 0 27 | 28 | #define SELF_TEST_X_GYRO 0x00 29 | #define SELF_TEST_Y_GYRO 0x01 30 | #define SELF_TEST_Z_GYRO 0x02 31 | #define SELF_TEST_X_ACCEL 0x0D 32 | #define SELF_TEST_Y_ACCEL 0x0E 33 | #define SELF_TEST_Z_ACCEL 0x0F 34 | 35 | #define SELF_TEST_A 0x10 36 | 37 | #define XG_OFFSET_H 0x13 // User-defined trim values for gyroscope 38 | #define XG_OFFSET_L 0x14 39 | #define YG_OFFSET_H 0x15 40 | #define YG_OFFSET_L 0x16 41 | #define ZG_OFFSET_H 0x17 42 | #define ZG_OFFSET_L 0x18 43 | #define SMPLRT_DIV 0x19 44 | #define CONFIG 0x1A 45 | #define GYRO_CONFIG 0x1B 46 | #define ACCEL_CONFIG 0x1C 47 | #define ACCEL_CONFIG2 0x1D 48 | #define LP_ACCEL_ODR 0x1E 49 | #define WOM_THR 0x1F 50 | 51 | #define MOT_DUR 0x20 // Duration counter threshold for motion interrupt generation, 1 kHz rate, LSB = 1 ms 52 | #define ZMOT_THR 0x21 // Zero-motion detection threshold bits [7:0] 53 | #define ZRMOT_DUR 0x22 // Duration counter threshold for zero motion interrupt generation, 16 Hz rate, LSB = 64 ms 54 | 55 | #define FIFO_EN 0x23 56 | #define I2C_MST_CTRL 0x24 57 | #define I2C_SLV0_ADDR 0x25 58 | #define I2C_SLV0_REG 0x26 59 | #define I2C_SLV0_CTRL 0x27 60 | #define I2C_SLV1_ADDR 0x28 61 | #define I2C_SLV1_REG 0x29 62 | #define I2C_SLV1_CTRL 0x2A 63 | #define I2C_SLV2_ADDR 0x2B 64 | #define I2C_SLV2_REG 0x2C 65 | #define I2C_SLV2_CTRL 0x2D 66 | #define I2C_SLV3_ADDR 0x2E 67 | #define I2C_SLV3_REG 0x2F 68 | #define I2C_SLV3_CTRL 0x30 69 | #define I2C_SLV4_ADDR 0x31 70 | #define I2C_SLV4_REG 0x32 71 | #define I2C_SLV4_DO 0x33 72 | #define I2C_SLV4_CTRL 0x34 73 | #define I2C_SLV4_DI 0x35 74 | #define I2C_MST_STATUS 0x36 75 | #define INT_PIN_CFG 0x37 76 | #define INT_ENABLE 0x38 77 | #define DMP_INT_STATUS 0x39 // Check DMP interrupt 78 | #define INT_STATUS 0x3A 79 | #define ACCEL_XOUT_H 0x3B 80 | #define ACCEL_XOUT_L 0x3C 81 | #define ACCEL_YOUT_H 0x3D 82 | #define ACCEL_YOUT_L 0x3E 83 | #define ACCEL_ZOUT_H 0x3F 84 | #define ACCEL_ZOUT_L 0x40 85 | #define TEMP_OUT_H 0x41 86 | #define TEMP_OUT_L 0x42 87 | #define GYRO_XOUT_H 0x43 88 | #define GYRO_XOUT_L 0x44 89 | #define GYRO_YOUT_H 0x45 90 | #define GYRO_YOUT_L 0x46 91 | #define GYRO_ZOUT_H 0x47 92 | #define GYRO_ZOUT_L 0x48 93 | #define EXT_SENS_DATA_00 0x49 94 | #define EXT_SENS_DATA_01 0x4A 95 | #define EXT_SENS_DATA_02 0x4B 96 | #define EXT_SENS_DATA_03 0x4C 97 | #define EXT_SENS_DATA_04 0x4D 98 | #define EXT_SENS_DATA_05 0x4E 99 | #define EXT_SENS_DATA_06 0x4F 100 | #define EXT_SENS_DATA_07 0x50 101 | #define EXT_SENS_DATA_08 0x51 102 | #define EXT_SENS_DATA_09 0x52 103 | #define EXT_SENS_DATA_10 0x53 104 | #define EXT_SENS_DATA_11 0x54 105 | #define EXT_SENS_DATA_12 0x55 106 | #define EXT_SENS_DATA_13 0x56 107 | #define EXT_SENS_DATA_14 0x57 108 | #define EXT_SENS_DATA_15 0x58 109 | #define EXT_SENS_DATA_16 0x59 110 | #define EXT_SENS_DATA_17 0x5A 111 | #define EXT_SENS_DATA_18 0x5B 112 | #define EXT_SENS_DATA_19 0x5C 113 | #define EXT_SENS_DATA_20 0x5D 114 | #define EXT_SENS_DATA_21 0x5E 115 | #define EXT_SENS_DATA_22 0x5F 116 | #define EXT_SENS_DATA_23 0x60 117 | #define MOT_DETECT_STATUS 0x61 118 | #define I2C_SLV0_DO 0x63 119 | #define I2C_SLV1_DO 0x64 120 | #define I2C_SLV2_DO 0x65 121 | #define I2C_SLV3_DO 0x66 122 | #define I2C_MST_DELAY_CTRL 0x67 123 | #define SIGNAL_PATH_RESET 0x68 124 | #define MOT_DETECT_CTRL 0x69 125 | #define USER_CTRL 0x6A // Bit 7 enable DMP, bit 3 reset DMP 126 | #define PWR_MGMT_1 0x6B // Device defaults to the SLEEP mode 127 | #define PWR_MGMT_2 0x6C 128 | #define DMP_BANK 0x6D // Activates a specific bank in the DMP 129 | #define DMP_RW_PNT 0x6E // Set read/write pointer to a specific start address in specified DMP bank 130 | #define DMP_REG 0x6F // Register in DMP from which to read or to which to write 131 | #define DMP_REG_1 0x70 132 | #define DMP_REG_2 0x71 133 | #define FIFO_COUNTH 0x72 134 | #define FIFO_COUNTL 0x73 135 | #define FIFO_R_W 0x74 136 | #define WHO_AM_I_MPU9250 0x75 // Should return 0x71 137 | #define XA_OFFSET_H 0x77 138 | #define XA_OFFSET_L 0x78 139 | #define YA_OFFSET_H 0x7A 140 | #define YA_OFFSET_L 0x7B 141 | #define ZA_OFFSET_H 0x7D 142 | #define ZA_OFFSET_L 0x7E 143 | 144 | // Set initial input parameters 145 | #define AFS_2G 0 146 | #define AFS_4G 1 147 | #define AFS_8G 2 148 | #define AFS_16G 3 149 | 150 | #define GFS_250DPS 0 151 | #define GFS_500DPS 1 152 | #define GFS_1000DPS 2 153 | #define GFS_2000DPS 3 154 | 155 | #define MFS_14BITS 0 // 0.6 mG per LSB 156 | #define MFS_16BITS 1 // 0.15 mG per LSB 157 | 158 | #define M_8Hz 0x02 159 | #define M_100Hz 0x06 160 | 161 | extern float yaw; 162 | extern float pitch; 163 | extern float roll; 164 | extern float temperature; 165 | 166 | extern uint16_t mpu_error; 167 | 168 | void mpu9250Setup(); 169 | void mpu9250Loop(); 170 | 171 | #endif -------------------------------------------------------------------------------- /network.cpp: -------------------------------------------------------------------------------- 1 | // network 2 | #include 3 | #include 4 | 5 | #include "network.h" 6 | // DNSServer dnsServer; 7 | 8 | /* Soft AP network parameters */ 9 | // IPAddress apIP(192, 168, 4, 1); 10 | // IPAddress subnet(255, 255, 255, 0); 11 | // IPAddress gateway(192, 168, 4, 1); 12 | 13 | void networkSetup() 14 | { 15 | static byte c = 0; 16 | static byte l = 30; // wifi connect timeout limit 17 | 18 | WiFi.mode(WIFI_AP_STA); 19 | 20 | // Serial.print("Setting soft-AP configuration ... "); 21 | // Serial.println(WiFi.softAPConfig(apIP, gateway, subnet) ? "Ready" : "Failed!"); 22 | 23 | Serial.println(); 24 | 25 | Serial.print("Starting Soft-AP ... "); 26 | Serial.println(WiFi.softAP(APssid, APpassword) ? "Success" : "Failed!"); 27 | 28 | Serial.print("Soft-AP IP address: "); 29 | Serial.println(WiFi.softAPIP()); 30 | 31 | WiFi.hostname(APssid); 32 | 33 | // dnsServer.setTTL(300); 34 | // dnsServer.setErrorReplyCode(DNSReplyCode::ServerFailure); 35 | // dnsServer.start(53, "*", apIP); 36 | // Serial.println("DNS started!"); 37 | 38 | if (strlen(ssid) > 0) 39 | { 40 | Serial.print("Connecting to " + String(ssid) + " "); 41 | WiFi.begin(ssid, password); 42 | while (c < l) 43 | { 44 | if (WiFi.status() == WL_CONNECTED) 45 | { 46 | Serial.println(" Success"); 47 | 48 | Serial.print("Local IP address: "); 49 | Serial.println(WiFi.localIP()); 50 | break; 51 | } 52 | delay(500); 53 | Serial.print("."); 54 | c++; 55 | } 56 | if (c >= l) 57 | { 58 | Serial.println(" Failed!"); 59 | } 60 | } 61 | 62 | Serial.print("Starting MDNS (" + String(APPCODE) + ".local) ... "); 63 | Serial.println(MDNS.begin(APPCODE) ? "Success." : "Failed!"); 64 | MDNS.addService("http", "tcp", 80); 65 | } 66 | 67 | void networkLoop() 68 | { 69 | // dns server 70 | // dnsServer.processNextRequest(); 71 | MDNS.update(); 72 | } 73 | -------------------------------------------------------------------------------- /network.h: -------------------------------------------------------------------------------- 1 | #ifndef NETWORK_H 2 | #define NETWORK_H 3 | 4 | #include "app.h" 5 | 6 | void networkSetup(); 7 | void networkLoop(); 8 | 9 | #endif -------------------------------------------------------------------------------- /ntp.cpp: -------------------------------------------------------------------------------- 1 | // ntp 2 | #include 3 | #include 4 | 5 | #include "ntp.h" 6 | 7 | WiFiUDP ntpUDP; 8 | NTPClient timeClient(ntpUDP, "pool.ntp.org"); 9 | 10 | void ntpSetup() 11 | { 12 | Serial.print("Starting NTP Client ... "); 13 | timeClient.begin(); 14 | // timeClient.setTimeOffset(28800); // GMT +8 = 8 x 3600 15 | Serial.println("Done"); 16 | } 17 | 18 | void ntpLoop() 19 | { 20 | timeClient.update(); 21 | epoch = timeClient.getEpochTime(); 22 | } 23 | -------------------------------------------------------------------------------- /ntp.h: -------------------------------------------------------------------------------- 1 | #ifndef NTP_H 2 | #define NTP_H 3 | 4 | #include "app.h" 5 | 6 | void ntpSetup(); 7 | void ntpLoop(); 8 | 9 | #endif -------------------------------------------------------------------------------- /tracker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "tracker.h" 7 | 8 | float sat_latitude; 9 | float sat_longitude; 10 | float sat_altitude; 11 | float sat_azimuth; 12 | float sat_elevation; 13 | float sat_distance; 14 | 15 | String catalog_number = "25544"; 16 | 17 | uint16_t get_error = 0; 18 | int http_code = 0; 19 | 20 | Sgp4 sat; 21 | 22 | String getValue(String data, char separator, int index) 23 | { 24 | int found = 0; 25 | int strIndex[] = {0, -1}; 26 | int maxIndex = data.length() - 1; 27 | 28 | data.replace("\r", ""); 29 | for (int i = 0; i <= maxIndex && found <= index; i++) 30 | { 31 | if (data.charAt(i) == separator || i == maxIndex) 32 | { 33 | found++; 34 | strIndex[0] = strIndex[1] + 1; 35 | strIndex[1] = (i == maxIndex) ? i + 1 : i; 36 | } 37 | } 38 | return found > index ? data.substring(strIndex[0], strIndex[1]) : ""; 39 | } 40 | 41 | void getTLE(String catalog_number) 42 | { 43 | // WiFiClient client; // http 44 | WiFiClientSecure client; // https 45 | HTTPClient http; 46 | 47 | if (WiFi.status() == WL_CONNECTED) 48 | { 49 | // https insecure 50 | client.setInsecure(); 51 | // if (http_timeout > 0) http.setTimeout(http_timeout); // ms 52 | Serial.print("Getting TLE ... "); 53 | if (http.begin(client, "https://celestrak.org/NORAD/elements/gp.php?CATNR=" + catalog_number)) 54 | { 55 | int httpCode = http.GET(); 56 | if (httpCode == HTTP_CODE_OK) 57 | { 58 | String payload = http.getString(); 59 | Serial.println("success"); 60 | 61 | String line1 = getValue(payload, '\n', 0); 62 | String line2 = getValue(payload, '\n', 1); 63 | String line3 = getValue(payload, '\n', 2); 64 | 65 | line1.toCharArray(tle_name, 32); 66 | line2.toCharArray(tle_line1, 70); 67 | line3.toCharArray(tle_line2, 70); 68 | 69 | sat.init(tle_name, tle_line1, tle_line2); // initialize satellite parameters 70 | } 71 | else 72 | { 73 | Serial.println("failed"); 74 | // Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str()); 75 | if (get_error < 65500) 76 | get_error++; 77 | } 78 | http.end(); 79 | http_code = httpCode; 80 | } 81 | } 82 | } 83 | 84 | void trackerSetup() 85 | { 86 | Serial.print("Loading TLE ... "); 87 | sat.site(latitude, longitude, altitude); // set site latitude[°], longitude[°] and altitude[m] 88 | sat.init(tle_name, tle_line1, tle_line2); // initialize satellite parameters 89 | Serial.println("done"); 90 | // Serial.println("latitude = " + String(latitude, 7) + " longitude = " + String(longitude, 7) + " altitude = " + String(altitude, 2)); 91 | // Serial.println("sat_name = " + String(sat_name)); 92 | // Serial.println("tle_line1 = " + String(tle_line1)); 93 | // Serial.println("tle_line2 = " + String(tle_line2)); 94 | // sat.findsat(timeNow); 95 | // sat.findsat(unixtime); 96 | // invjday(sat.satJd, timeZone, true, year, mon, day, hr, minute, sec); 97 | // Serial.println("\nLocal time: " + String(day) + '/' + String(mon) + '/' + String(year) + ' ' + String(hr) + ':' + String(minute) + ':' + String(sec)); 98 | // Serial.println("azimuth = " + String(sat.satAz) + " elevation = " + String(sat.satEl) + " distance = " + String(sat.satDist)); 99 | // Serial.println("latitude = " + String(sat.satLat) + " longitude = " + String(sat.satLon) + " altitude = " + String(sat.satAlt)); 100 | // Serial.println("AZStep pos: " + String(stepperAZ.currentPosition())); 101 | } 102 | 103 | void trackerLoop() 104 | { 105 | // int timezone = 8 ; //utc + 8 106 | // int _year; 107 | // int _month; 108 | // int _day; 109 | // int _hour; 110 | // int _minute; 111 | // double _second; 112 | 113 | sat.findsat((unsigned long)epoch); 114 | // invjday(sat.satJd, timezone, true, _year, _month, _day, _hour, _minute, _second); 115 | // Serial.println(String(day) + '/' + String(mon) + '/' + String(year) + ' ' + String(hr) + ':' + String(minute) + ':' + String(sec)); 116 | // Serial.println("azimuth = " + String(sat.satAz) + " elevation = " + String(sat.satEl) + " distance = " + String(sat.satDist)); 117 | // Serial.println("latitude = " + String(sat.satLat) + " longitude = " + String(sat.satLon) + " altitude = " + String(sat.satAlt)); 118 | // Serial.println("AZStep pos: " + String(stepperAZ.currentPosition())); 119 | sat_latitude = sat.satLat; 120 | sat_longitude = sat.satLon; 121 | sat_altitude = sat.satAlt; 122 | sat_azimuth = sat.satAz; 123 | sat_elevation = sat.satEl; 124 | sat_distance = sat.satDist; 125 | 126 | // second = _second; 127 | // minute = _minute; 128 | // hour = _hour; 129 | // day = _day; 130 | // month = _month; 131 | // year = _year; 132 | 133 | if (auto_control) 134 | { 135 | setAz(round(sat_azimuth)); // 0 - 360 136 | setEl(round(sat_elevation)); // 0 - 90 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tracker.h: -------------------------------------------------------------------------------- 1 | #ifndef TRACKER_H 2 | #define TRACKER_H 3 | 4 | #include "eeprom.h" 5 | #include "app.h" 6 | 7 | extern float sat_latitude; 8 | extern float sat_longitude; 9 | extern float sat_altitude; 10 | extern float sat_azimuth; 11 | extern float sat_elevation; 12 | extern float sat_distance; 13 | 14 | extern String catalog_number; 15 | 16 | extern uint16_t get_error; 17 | extern int http_code; 18 | 19 | void getTLE(String catalog_number); 20 | void trackerSetup(); 21 | void trackerLoop(); 22 | 23 | #endif -------------------------------------------------------------------------------- /webserver.cpp: -------------------------------------------------------------------------------- 1 | // webserver 2 | #include 3 | #include "ArduinoJson.h" 4 | 5 | #include "webserver.h" 6 | 7 | ESP8266WebServer webServer(80); 8 | 9 | int getRSSIasQuality(int RSSI) 10 | { 11 | int quality = 0; 12 | 13 | if (RSSI <= -100) 14 | { 15 | quality = 0; 16 | } 17 | else if (RSSI >= -50) 18 | { 19 | quality = 100; 20 | } 21 | else 22 | { 23 | quality = 2 * (RSSI + 100); 24 | } 25 | return quality; 26 | } 27 | 28 | void webserverSetup() 29 | { 30 | webServer.on("/", HTTP_GET, []() 31 | { webServer.send(200, "text/html", index_html); }); 32 | webServer.on("/system", HTTP_POST, []() 33 | { 34 | byte expand_system = 0; 35 | byte expand_wifiap = 1; 36 | byte expand_wifista = 1; 37 | byte expand_command = 1; 38 | 39 | if (webServer.arg("plain") != "{}") { 40 | DynamicJsonDocument doc(1024); 41 | deserializeJson(doc, webServer.arg("plain").c_str()); 42 | 43 | if (doc["expand_system"]) expand_system = doc["expand_system"]; 44 | if (doc["expand_wifiap"]) expand_wifiap = doc["expand_wifiap"]; 45 | if (doc["expand_wifista"]) expand_wifista = doc["expand_wifista"]; 46 | if (doc["expand_command"]) expand_command = doc["expand_command"]; 47 | 48 | if (doc["reboot"]) ESP.restart(); 49 | } 50 | 51 | String json; 52 | 53 | json += "["; 54 | json += "{\"label\":\"System\",\"name\":\"expand_system\",\"value\":"+String(expand_system)+",\"elements\":["; 55 | json += "{\"type\":\"text\",\"label\":\"Chip ID\",\"name\":\"chip_id\",\"value\":\""+String(ESP.getChipId())+"\",\"attrib\":\"disabled\"},"; 56 | json += "{\"type\":\"text\",\"label\":\"Free Heap\",\"name\":\"free_heap\",\"value\":\""+String(ESP.getFreeHeap())+"\",\"attrib\":\"disabled\"},"; 57 | json += "{\"type\":\"text\",\"label\":\"Flash ID\",\"name\":\"flash_id\",\"value\":\""+String(ESP.getFlashChipId())+"\",\"attrib\":\"disabled\"},"; 58 | json += "{\"type\":\"text\",\"label\":\"Flash Size\",\"name\":\"flash_size\",\"value\":\""+String(ESP.getFlashChipSize())+"\",\"attrib\":\"disabled\"}"; 59 | //json += "{\"type\":\"text\",\"label\":\"Flash Real Size\",\"name\":\"flash_real_size\",\"value\":\""+String(ESP.getFlashChipRealSize())+"\",\"attrib\":\"disabled\"}"; 60 | json += "]},"; 61 | json += "{\"label\":\"Wifi AP\",\"name\":\"expand_wifiap\",\"value\":"+String(expand_wifiap)+",\"elements\":["; 62 | json += "{\"type\":\"text\",\"label\":\"AP MAC\",\"name\":\"ap_mac\",\"value\":\""+WiFi.softAPmacAddress()+"\",\"attrib\":\"disabled\"},"; 63 | json += "{\"type\":\"text\",\"label\":\"AP Address\",\"name\":\"ap_address\",\"value\":\""+WiFi.softAPIP().toString()+"\",\"attrib\":\"disabled\"},"; 64 | json += "{\"type\":\"text\",\"label\":\"AP SSID\",\"name\":\"ap_ssid\",\"value\":\""+String(APssid)+"\",\"attrib\":\"disabled\"},"; 65 | json += "{\"type\":\"text\",\"label\":\"Connected Devices\",\"name\":\"connected_devices\",\"value\":\""+String(WiFi.softAPgetStationNum())+"\",\"attrib\":\"disabled\"}"; 66 | json += "]},"; 67 | json += "{\"label\":\"Wifi Station\",\"name\":\"expand_wifista\",\"value\":"+String(expand_wifista)+",\"elements\":["; 68 | json += "{\"type\":\"text\",\"label\":\"MAC\",\"name\":\"mac\",\"value\":\""+WiFi.macAddress()+"\",\"attrib\":\"disabled\"},"; 69 | json += "{\"type\":\"text\",\"label\":\"Address\",\"name\":\"address\",\"value\":\""+WiFi.localIP().toString()+"\",\"attrib\":\"disabled\"},"; 70 | json += "{\"type\":\"text\",\"label\":\"SSID\",\"name\":\"ssid\",\"value\":\""+WiFi.SSID()+"\",\"attrib\":\"disabled\"}"; 71 | json += "]},"; 72 | json += "{\"label\":\"Command\",\"name\":\"expand_command\",\"value\":"+String(expand_command)+",\"elements\":["; 73 | json += "{\"type\":\"button\",\"label\":\"REBOOT\",\"name\":\"reboot\",\"value\":\"reboot\",\"confirm\":\"Are you sure you want to reboot?\"}"; 74 | json += "]}"; 75 | json += "]"; 76 | 77 | webServer.sendHeader("Access-Control-Allow-Origin", "*"); 78 | webServer.send(200, "application/json", json); }); 79 | webServer.on("/scan", HTTP_GET, []() 80 | { 81 | String json = ""; 82 | 83 | json += "["; 84 | int n = WiFi.scanComplete(); 85 | if (n == -2) { 86 | WiFi.scanNetworks(true); 87 | } else if(n) { 88 | for (int i = 0; i < n; ++i) { 89 | if (i) json += ","; 90 | json += "{"; 91 | json += "\"rssi\":" + String(WiFi.RSSI(i)); 92 | json += "\"signal\":" + String(getRSSIasQuality(WiFi.RSSI(i))); 93 | json += ",\"ssid\":\"" + WiFi.SSID(i) + "\""; 94 | json += ",\"bssid\":\"" + WiFi.BSSIDstr(i) + "\""; 95 | json += ",\"channel\":" + String(WiFi.channel(i)); 96 | json += ",\"secure\":" + String(WiFi.encryptionType(i)); 97 | json += ",\"hidden\":" + String(WiFi.isHidden(i) ? "true" : "false"); 98 | json += "}"; 99 | } 100 | WiFi.scanDelete(); 101 | if (WiFi.scanComplete() == -2) { 102 | WiFi.scanNetworks(true); 103 | } 104 | } 105 | json += "]"; 106 | 107 | webServer.sendHeader("Access-Control-Allow-Origin", "*"); 108 | webServer.send(200, "application/json", json); }); 109 | webServer.on("/config", HTTP_POST, []() 110 | { 111 | if (webServer.arg("plain") != "{}") { 112 | DynamicJsonDocument doc(1024); 113 | deserializeJson(doc, webServer.arg("plain").c_str()); 114 | 115 | if (doc["update"]) { 116 | if (doc["ssid"]) strcpy(ssid, (const char*)doc["ssid"]); 117 | if (doc["password"]) strcpy(password, (const char*)doc["password"]); 118 | 119 | if (doc["beep_enable"]) beep_enable = doc["beep_enable"]; 120 | if (doc["analog_enable"]) analog_enable = doc["analog_enable"]; 121 | if (doc["display_enable"]) display_enable = doc["display_enable"]; 122 | if (doc["ads1115_enable"]) ads1115_enable = doc["ads1115_enable"]; 123 | if (doc["mpu9250_enable"]) mpu9250_enable = doc["mpu9250_enable"]; 124 | 125 | if (doc["latitude"]) latitude = doc["latitude"]; 126 | if (doc["longitude"]) longitude = doc["longitude"]; 127 | if (doc["altitude"]) altitude = doc["altitude"]; 128 | if (doc["declination"]) declination = doc["declination"]; 129 | 130 | if (doc["tracker_enable"]) tracker_enable = doc["tracker_enable"]; 131 | if (doc["tle_name"]) strcpy(tle_name, (const char*)doc["tle_name"]); 132 | if (doc["tle_line1"]) strcpy(tle_line1, (const char*)doc["tle_line1"]); 133 | if (doc["tle_line2"]) strcpy(tle_line2, (const char*)doc["tle_line2"]); 134 | if (doc["auto_control"]) auto_control = doc["auto_control"]; 135 | 136 | if (doc["motor_enable"]) motor_enable = doc["motor_enable"]; 137 | if (doc["ms_per_deg"]) ms_per_deg = doc["ms_per_deg"]; 138 | 139 | saveConfig(); 140 | Serial.println("Save config"); 141 | } 142 | } 143 | 144 | String json; 145 | String sep; 146 | int n = WiFi.scanNetworks(); 147 | 148 | json += "["; 149 | json += "{\"label\":\"Wifi Connect\",\"name\":\"expand_wifi\",\"value\":1,\"elements\":["; 150 | json += "{\"type\":\"select\",\"label\":\"SSID\",\"name\":\"ssid\",\"value\":\"" + WiFi.SSID() + "\",\"options\":["; 151 | if (n > 0) { 152 | for (int i = 0; i < n; i++) { 153 | json += sep+"[\""+WiFi.SSID(i)+"\",\""+WiFi.SSID(i)+" "+String(getRSSIasQuality(WiFi.RSSI(i)))+"%"; 154 | if (WiFi.encryptionType(i) != ENC_TYPE_NONE) { 155 | json += " ⚲"; 156 | } 157 | json += "\"]"; 158 | sep = ","; 159 | } 160 | } else { 161 | json += "[\"\",\"\"]"; 162 | } 163 | json += "]},"; 164 | json += "{\"type\":\"text\",\"label\":\"Password\",\"name\":\"password\",\"value\":\"" + String(password) + "\"}"; 165 | json += "]},"; 166 | 167 | json += "{\"label\":\"Components\",\"name\":\"expand_component\",\"value\":1,\"elements\":["; 168 | json += "{\"type\":\"select\",\"label\":\"Beep\",\"name\":\"beep_enable\",\"value\":\"" + String(beep_enable) + "\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]},"; 169 | json += "{\"type\":\"select\",\"label\":\"Analog\",\"name\":\"analog_enable\",\"value\":\"" + String(analog_enable) + "\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]},"; 170 | json += "{\"type\":\"select\",\"label\":\"Display\",\"name\":\"display_enable\",\"value\":\"" + String(display_enable) + "\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]},"; 171 | json += "{\"type\":\"select\",\"label\":\"ADS1115\",\"name\":\"ads1115_enable\",\"value\":\"" + String(ads1115_enable) + "\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]},"; 172 | json += "{\"type\":\"select\",\"label\":\"MPU9250\",\"name\":\"mpu9250_enable\",\"value\":\"" + String(mpu9250_enable) + "\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]}"; 173 | json += "]},"; 174 | 175 | json += "{\"label\":\"My Location\",\"name\":\"expand_location\",\"value\":1,\"elements\":["; 176 | json += "{\"type\":\"text\",\"label\":\"Latitude\",\"name\":\"latitude\",\"value\":\"" + String(latitude, 7) + "\"},"; 177 | json += "{\"type\":\"text\",\"label\":\"Longitude\",\"name\":\"longitude\",\"value\":\"" + String(longitude, 7) + "\"},"; 178 | json += "{\"type\":\"text\",\"label\":\"Altitude\",\"name\":\"altitude\",\"value\":\"" + String(altitude) + "\"},"; 179 | json += "{\"type\":\"text\",\"label\":\"Declination\",\"name\":\"declination\",\"value\":\"" + String(declination) + "\"}"; 180 | json += "]},"; 181 | 182 | json += "{\"label\":\"Tracker\",\"name\":\"expand_tracker\",\"value\":1,\"elements\":["; 183 | json += "{\"type\":\"select\",\"label\":\"Tracker\",\"name\":\"tracker_enable\",\"value\":\"" + String(tracker_enable) + "\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]},"; 184 | json += "{\"type\":\"text\",\"label\":\"TLE Name\",\"name\":\"tle_name\",\"value\":\"" + String(tle_name) + "\"},"; 185 | json += "{\"type\":\"text\",\"label\":\"TLE Line1\",\"name\":\"tle_line1\",\"value\":\"" + String(tle_line1) + "\"},"; 186 | json += "{\"type\":\"text\",\"label\":\"TLE Line2\",\"name\":\"tle_line2\",\"value\":\"" + String(tle_line2) + "\"},"; 187 | json += "{\"type\":\"select\",\"label\":\"Auto Control\",\"name\":\"auto_control\",\"value\":\"" + String(auto_control) + "\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]}"; 188 | json += "]},"; 189 | 190 | json += "{\"label\":\"Motor\",\"name\":\"expand_motor\",\"value\":1,\"elements\":["; 191 | json += "{\"type\":\"select\",\"label\":\"Motor\",\"name\":\"motor_enable\",\"value\":\"" + String(motor_enable) + "\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]},"; 192 | json += "{\"type\":\"text\",\"label\":\"msPerDeg\",\"name\":\"ms_per_deg\",\"value\":\"" + String(ms_per_deg, 3) + "\"}"; 193 | json += "]},"; 194 | 195 | json += "{\"label\":\"Page\",\"name\":\"expand_page\",\"value\":1,\"elements\":["; 196 | json += "{\"type\":\"button\",\"label\":\"UPDATE\",\"name\":\"update\",\"value\":\"update\"}"; 197 | json += "]}"; 198 | json += "]"; 199 | 200 | webServer.sendHeader("Access-Control-Allow-Origin", "*"); 201 | webServer.send(200, "application/json", json); }); 202 | webServer.on("/app", HTTP_POST, []() 203 | { 204 | byte refresh = 2; 205 | int degreeAz = 0; 206 | int degreeEl = 0; 207 | 208 | byte expand_location = 0; 209 | byte expand_satellite = 1; 210 | byte expand_mpu9250 = 1; 211 | byte change_satellite = 0; 212 | byte expand_motor = 1; 213 | byte expand_motor1 = 1; 214 | byte expand_motor2 = 1; 215 | byte expand_errors = 0; 216 | byte expand_page = 0; 217 | 218 | if (webServer.arg("plain") != "{}") { 219 | DynamicJsonDocument doc(1024); 220 | deserializeJson(doc, webServer.arg("plain").c_str()); 221 | 222 | if (doc["actionMotor"]) { 223 | int action = doc["actionMotor"]; 224 | if (action == 5) { // enter 225 | stopAz(); 226 | stopEl(); 227 | motor_az = 0.0; 228 | motor_el = 0.0; 229 | } 230 | else if (action == 1) forwardEl(); // up 231 | else if (action == 2) reverseEl(); // down 232 | else if (action == 3) reverseAz(); // left 233 | else if (action == 4) forwardAz(); // right 234 | //else if (action == 5) { 235 | // forwardAz(); 236 | // forwardEl(); 237 | //} 238 | //else if (action == 6) { 239 | // reverseAz(); 240 | // reverseEl(); 241 | //} 242 | } 243 | else if (doc["actionAz"]) { 244 | if (doc["degreeAz"]) { 245 | degreeAz = doc["degreeAz"]; 246 | Serial.print("rotateAz "); 247 | Serial.println(degreeAz); 248 | setAz(degreeAz); 249 | } 250 | } 251 | else if (doc["actionEl"]) { 252 | if (doc["degreeEl"]) { 253 | degreeEl = doc["degreeEl"]; 254 | Serial.print("rotateEl "); 255 | Serial.println(degreeEl); 256 | setEl(degreeEl); 257 | } 258 | } 259 | 260 | if (doc["expand_location"]) expand_location = doc["expand_location"]; 261 | if (doc["expand_satellite"]) expand_satellite = doc["expand_satellite"]; 262 | if (doc["expand_mpu9250"]) expand_mpu9250 = doc["expand_mpu9250"]; 263 | if (doc["expand_motor"]) expand_motor = doc["expand_motor"]; 264 | if (doc["expand_motor1"]) expand_motor1 = doc["expand_motor1"]; 265 | if (doc["expand_motor2"]) expand_motor2 = doc["expand_motor2"]; 266 | if (doc["change_satellite"]) change_satellite = doc["change_satellite"]; 267 | if (doc["expand_errors"]) expand_errors = doc["expand_errors"]; 268 | if (doc["expand_page"]) expand_page = doc["expand_page"]; 269 | 270 | if (doc["update"]) { 271 | if (doc["catalog_number"]) catalog_number = String(doc["catalog_number"]); 272 | //getTLE(catalog_number); 273 | getTLE("37839"); 274 | if (doc["auto_control"]) auto_control = doc["auto_control"]; 275 | } 276 | 277 | if (doc["refresh"]) refresh = doc["refresh"]; 278 | } 279 | 280 | String json; 281 | String disabled = "false"; 282 | 283 | json += "["; 284 | if (analog_enable) { 285 | json += "{\"label\":\"Analog Sensor\",\"name\":\"expand_analog\",\"value\":1,\"elements\":["; 286 | json += "{\"type\":\"text\",\"label\":\"Volt\",\"name\":\"device_voltage\",\"value\":\""+String(a_voltage)+"\",\"attrib\":\"disabled\"}"; 287 | json += "]},"; 288 | } 289 | 290 | if (mpu9250_enable) { 291 | json += "{\"label\":\"MPU9250\",\"name\":\"expand_mpu9250\",\"value\":"+String(expand_mpu9250)+",\"elements\":["; 292 | json += "{\"type\":\"text\",\"label\":\"Yaw\",\"name\":\"yaw\",\"value\":\""+String(yaw)+"\",\"attrib\":\"disabled\"},"; 293 | json += "{\"type\":\"text\",\"label\":\"Pitch\",\"name\":\"pitch\",\"value\":\""+String(pitch)+"\",\"attrib\":\"disabled\"},"; 294 | json += "{\"type\":\"text\",\"label\":\"Roll\",\"name\":\"roll\",\"value\":\""+String(roll)+"\",\"attrib\":\"disabled\"},"; 295 | json += "{\"type\":\"text\",\"label\":\"Temperature\",\"name\":\"temperature\",\"value\":\""+String(temperature)+"\",\"attrib\":\"disabled\"}"; 296 | json += "]},"; 297 | } 298 | 299 | json += "{\"label\":\"My Location\",\"name\":\"expand_location\",\"value\":"+String(expand_location)+",\"elements\":["; 300 | json += "{\"type\":\"text\",\"label\":\"Latitude\",\"name\":\"latitude\",\"value\":\""+String(latitude,7)+"\",\"attrib\":\"disabled\"},"; 301 | json += "{\"type\":\"text\",\"label\":\"Longitude\",\"name\":\"longitude\",\"value\":\""+String(longitude,7)+"\",\"attrib\":\"disabled\"},"; 302 | json += "{\"type\":\"text\",\"label\":\"Altitude\",\"name\":\"altitude\",\"value\":\""+String(altitude)+"\",\"attrib\":\"disabled\"},"; 303 | json += "{\"type\":\"text\",\"label\":\"Declination\",\"name\":\"declination\",\"value\":\""+String(declination)+"\",\"attrib\":\"disabled\"}"; 304 | json += "]},"; 305 | 306 | json += "{\"label\":\"Satellite\",\"name\":\"expand_satellite\",\"value\":"+String(expand_satellite)+",\"elements\":["; 307 | json += "{\"type\":\"text\",\"label\":\"Name\",\"name\":\"tle_name\",\"value\":\""+String(tle_name)+"\",\"attrib\":\"disabled\"},"; 308 | json += "{\"type\":\"text\",\"label\":\"Epoch\",\"name\":\"epoch\",\"value\":\""+String(epoch)+"\",\"attrib\":\"disabled\"},"; 309 | json += "{\"type\":\"text\",\"label\":\"Latitude\",\"name\":\"sat_latitude\",\"value\":\""+String(sat_latitude,7)+"\",\"attrib\":\"disabled\"},"; 310 | json += "{\"type\":\"text\",\"label\":\"Longitude\",\"name\":\"sat_longitude\",\"value\":\""+String(sat_longitude,7)+"\",\"attrib\":\"disabled\"},"; 311 | json += "{\"type\":\"text\",\"label\":\"Altitude\",\"name\":\"sat_altitude\",\"value\":\""+String(sat_altitude)+"\",\"attrib\":\"disabled\"},"; 312 | json += "{\"type\":\"text\",\"label\":\"Azimuth\",\"name\":\"sat_azimuth\",\"value\":\""+String(sat_azimuth)+"\",\"attrib\":\"disabled\"},"; 313 | json += "{\"type\":\"text\",\"label\":\"Elevation\",\"name\":\"sat_elevation\",\"value\":\""+String(sat_elevation)+"\",\"attrib\":\"disabled\"},"; 314 | json += "{\"type\":\"text\",\"label\":\"Distance\",\"name\":\"sat_distance\",\"value\":\""+String(sat_distance)+"\",\"attrib\":\"disabled\"}"; 315 | json += "]},"; 316 | 317 | json += "{\"label\":\"Change Satellite\",\"name\":\"change_satellite\",\"value\":"+String(change_satellite)+",\"elements\":["; 318 | json += "{\"type\":\"text\",\"label\":\"Catalog Number\",\"name\":\"catalog_number\",\"value\":\""+catalog_number+"\"},"; 319 | json += "{\"type\":\"select\",\"label\":\"Auto Control\",\"name\":\"auto_control\",\"value\":\""+String(auto_control)+"\",\"options\":[[\"0\",\"Disabled\"],[\"1\",\"Enabled\"]]},"; 320 | json += "{\"type\":\"button\",\"label\":\"UPDATE\",\"name\":\"update\",\"value\":\"update\"}"; 321 | json += "]},"; 322 | 323 | json += "{\"label\":\"Motor\",\"name\":\"expand_motor\",\"value\":"+String(expand_motor)+",\"elements\":["; 324 | json += "{\"type\":\"text\",\"label\":\"Motor Mode Az\",\"name\":\"motor_mode_az\",\"value\":\""+String(motor_mode_az)+"\",\"attrib\":\"disabled\"},"; 325 | json += "{\"type\":\"text\",\"label\":\"Motor Az\",\"name\":\"motor_az\",\"value\":\""+String(motor_az)+"\",\"attrib\":\"disabled\"},"; 326 | json += "{\"type\":\"text\",\"label\":\"Motor Mode El\",\"name\":\"motor_mode_el\",\"value\":\""+String(motor_mode_el)+"\",\"attrib\":\"disabled\"},"; 327 | json += "{\"type\":\"text\",\"label\":\"Motor El\",\"name\":\"motor_el\",\"value\":\""+String(motor_el)+"\",\"attrib\":\"disabled\"}"; 328 | json += "]},"; 329 | 330 | json += "{\"label\":\"Motor Control 1\",\"name\":\"expand_motor1\",\"value\":"+String(expand_motor1)+",\"elements\":["; 331 | json += "{\"type\":\"text\",\"label\":\"Az Degree\",\"name\":\"degreeAz\",\"value\":\""+String(degreeAz)+"\"},"; 332 | disabled = motor_mode_az == 0 ? "false" : "true"; 333 | json += "{\"type\":\"button\",\"label\":\"Set Az\",\"name\":\"actionAz\",\"value\":\"1\",\"disabled\":\""+disabled+"\"},"; 334 | json += "{\"type\":\"text\",\"label\":\"El Degree\",\"name\":\"degreeEl\",\"value\":\""+String(degreeEl)+"\"},"; 335 | disabled = motor_mode_el == 0 ? "false" : "true"; 336 | json += "{\"type\":\"button\",\"label\":\"Set El\",\"name\":\"actionEl\",\"value\":\"1\",\"disabled\":\""+disabled+"\"}"; 337 | json += "]},"; 338 | 339 | json += "{\"label\":\"Motor Control 2\",\"name\":\"expand_motor2\",\"value\":"+String(expand_motor2)+",\"elements\":["; 340 | json += "{\"type\":\"arrows\",\"name\":\"actionMotor\"}"; 341 | json += "]},"; 342 | 343 | json += "{\"label\":\"Error\",\"name\":\"expand_errors\",\"value\":"+String(expand_errors)+",\"elements\":["; 344 | json += "{\"type\":\"text\",\"label\":\"Get\",\"name\":\"get_error\",\"value\":\""+String(get_error)+"\",\"attrib\":\"disabled\"},"; 345 | json += "{\"type\":\"text\",\"label\":\"Http Code\",\"name\":\"http_code\",\"value\":\""+String(http_code)+"\",\"attrib\":\"disabled\"}"; 346 | json += "]},"; 347 | 348 | json += "{\"label\":\"Page\",\"name\":\"expand_page\",\"value\":"+String(expand_page)+",\"elements\":["; 349 | json += "{\"type\":\"refresh\",\"label\":\"Refresh\",\"name\":\"refresh\",\"value\":"+String(refresh)+"}"; 350 | json += "]}"; 351 | 352 | json += "]"; 353 | 354 | webServer.sendHeader("Access-Control-Allow-Origin", "*"); 355 | webServer.send(200, "application/json", json); }); 356 | webServer.on("/firmware", HTTP_POST, []() 357 | { 358 | String json; 359 | json += "["; 360 | json += "{\"label\":\"Firmware Upgrade\",\"name\":\"firmware_upgrade\",\"value\":\"1\",\"elements\":["; 361 | json += "{\"type\":\"file\",\"label\":\"File\",\"name\":\"file\",\"value\":\"\",\"accept\":\".bin\"},"; 362 | json += "{\"type\":\"button\",\"label\":\"UPLOAD\",\"name\":\"upload\",\"value\":\"upload\"}"; 363 | json += "]}"; 364 | json += "]"; 365 | 366 | webServer.sendHeader("Access-Control-Allow-Origin", "*"); 367 | webServer.send(200, "application/json", json); }); 368 | webServer.on( 369 | "/upload", HTTP_POST, []() 370 | { 371 | String json; 372 | json += "["; 373 | json += "{\"label\":\"Firmware Upgrade\",\"name\":\"firmware_upgrade\",\"value\":\"1\",\"elements\":["; 374 | if (Update.hasError()) 375 | json += "{\"type\":\"alert\",\"value\":\"Upload failed!\"},"; 376 | else 377 | json += "{\"type\":\"alert\",\"value\":\"Upload success!\"},"; 378 | 379 | json += "{\"type\":\"file\",\"label\":\"File\",\"name\":\"file\",\"value\":\"\",\"accept\":\".bin\"},"; 380 | json += "{\"type\":\"button\",\"label\":\"UPDATE\",\"name\":\"update\",\"value\":\"update\"}"; 381 | json += "]}"; 382 | json += "]"; 383 | webServer.sendHeader("Access-Control-Allow-Origin", "*"); 384 | webServer.send(200, "application/json", json); 385 | ESP.restart(); }, 386 | []() 387 | { 388 | HTTPUpload &upload = webServer.upload(); 389 | if (upload.status == UPLOAD_FILE_START) 390 | { 391 | Serial.setDebugOutput(true); 392 | // WiFiUDP::stopAll(); 393 | Serial.printf("Update: %s\n", upload.filename.c_str()); 394 | uint32_t maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000; 395 | if (!Update.begin(maxSketchSpace)) 396 | { // start with max available size 397 | Update.printError(Serial); 398 | } 399 | } 400 | else if (upload.status == UPLOAD_FILE_WRITE) 401 | { 402 | if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) 403 | { 404 | Update.printError(Serial); 405 | } 406 | } 407 | else if (upload.status == UPLOAD_FILE_END) 408 | { 409 | if (Update.end(true)) 410 | { // true to set the size to the current progress 411 | Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize); 412 | 413 | String json; 414 | json += "["; 415 | json += "{\"label\":\"Firmware Upgrade\",\"name\":\"firmware_upgrade\",\"value\":\"1\",\"elements\":["; 416 | if (Update.hasError()) 417 | json += "{\"type\":\"alert\",\"value\":\"Upload failed!\"},"; 418 | else 419 | json += "{\"type\":\"alert\",\"value\":\"Upload success!\"},"; 420 | 421 | json += "{\"type\":\"file\",\"label\":\"File\",\"name\":\"file\",\"value\":\"\",\"accept\":\".bin\"},"; 422 | json += "{\"type\":\"button\",\"label\":\"UPDATE\",\"name\":\"update\",\"value\":\"update\"}"; 423 | json += "]}"; 424 | json += "]"; 425 | webServer.sendHeader("Access-Control-Allow-Origin", "*"); 426 | webServer.send(200, "application/json", json); 427 | } 428 | else 429 | { 430 | Update.printError(Serial); 431 | } 432 | Serial.setDebugOutput(false); 433 | } 434 | yield(); 435 | }); 436 | // Send a POST request to /post with a form field message set to 437 | /* 438 | webServer.on("/post", HTTP_POST, []() { 439 | //nothing and dont remove it 440 | }, NULL, [](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { 441 | DynamicJsonDocument doc(1024); 442 | deserializeJson(doc, data); 443 | String json; 444 | 445 | serializeJson(doc, json); 446 | request->send(200, "application/json", json); 447 | }); 448 | */ 449 | webServer.onNotFound([]() 450 | { 451 | String json; 452 | //StaticJsonDocument<20> doc; 453 | //doc["message"] = "Endpoint not found"; 454 | //serializeJson(doc, json); 455 | json += "["; 456 | json += "{\"label\":\"404 Error\",\"name\":\"not_found\",\"value\":\"1\",\"elements\":["; 457 | json += "{\"type\":\"alert\",\"value\":\"Page not found!\"}"; 458 | json += "]}"; 459 | json += "]"; 460 | webServer.sendHeader("Access-Control-Allow-Origin", "*"); 461 | webServer.send(404, "application/json", json); }); 462 | 463 | Serial.print("Starting Webserver ... "); 464 | webServer.begin(); 465 | Serial.println("Done"); 466 | } 467 | 468 | void webserverLoop() 469 | { 470 | webServer.handleClient(); 471 | } 472 | -------------------------------------------------------------------------------- /webserver.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSERVER_H 2 | #define WEBSERVER_H 3 | 4 | #include "html.h" 5 | #include "tracker.h" 6 | #include "analog.h" 7 | #include "mpu9250.h" 8 | #include "app.h" 9 | 10 | void webserverSetup(); 11 | void webserverLoop(); 12 | 13 | #endif -------------------------------------------------------------------------------- /websocket.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "websocket.h" 4 | 5 | WebSocketsServer webSocket = WebSocketsServer(81); 6 | 7 | void webSocketEvent(uint8_t num, WStype_t type, uint8_t *payload, size_t length) 8 | { 9 | switch (type) 10 | { 11 | case WStype_DISCONNECTED: 12 | Serial.printf("[%u] Disconnected!\n", num); 13 | break; 14 | case WStype_CONNECTED: 15 | { 16 | IPAddress ip = webSocket.remoteIP(num); 17 | Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); 18 | 19 | // send message to client 20 | webSocket.sendTXT(num, "Connected"); 21 | } 22 | break; 23 | case WStype_TEXT: 24 | Serial.printf("[%u] get Text: %s\n", num, payload); 25 | 26 | // send message to client 27 | // webSocket.sendTXT(num, "message here"); 28 | 29 | // send data to all connected clients 30 | // webSocket.broadcastTXT("message here"); 31 | break; 32 | case WStype_BIN: 33 | Serial.printf("[%u] get binary length: %u\n", num, length); 34 | hexdump(payload, length); 35 | 36 | // send message to client 37 | // webSocket.sendBIN(num, payload, length); 38 | break; 39 | } 40 | } 41 | 42 | void websocketSetup() 43 | { 44 | webSocket.begin(); 45 | webSocket.onEvent(webSocketEvent); 46 | } 47 | 48 | void websocketLoop() 49 | { 50 | webSocket.loop(); 51 | } 52 | -------------------------------------------------------------------------------- /websocket.h: -------------------------------------------------------------------------------- 1 | #ifndef WEBSOCKET_H 2 | #define WEBSOCKET_H 3 | 4 | #include "html.h" 5 | #include "tracker.h" 6 | #include "mpu9250.h" 7 | #include "app.h" 8 | 9 | void websocketSetup(); 10 | void websocketLoop(); 11 | 12 | #endif --------------------------------------------------------------------------------