safwan092

Untitled

Oct 2nd, 2025
36
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.64 KB | None | 0 0
  1. #include <Wire.h>
  2.  
  3. const int MPU_ADDR = 0x68;
  4.  
  5. // MPU6050 register addresses
  6. const int PWR_MGMT_1 = 0x6B;
  7. const int ACCEL_XOUT_H = 0x3B;
  8. const int GYRO_XOUT_H = 0x43;
  9. const int TEMP_OUT_H = 0x41;
  10.  
  11. // Calibration offsets
  12. float accelOffsetX = 0;
  13. float accelOffsetY = 0;
  14. float accelOffsetZ = 0;
  15. float gyroOffsetX = 0;
  16. float gyroOffsetY = 0;
  17. float gyroOffsetZ = 0;
  18.  
  19. // Adaptive thresholds - will adjust based on normal motion
  20. float adaptiveAccelThreshold = 3.0; // Start with reasonable value
  21. float adaptiveGyroThreshold = 50.0; // Start with reasonable value
  22.  
  23. // Impact detection
  24. const unsigned long IMPACT_WINDOW = 100; // Time to analyze impact (ms)
  25. const unsigned long HIT_COOLDOWN = 2000; // Prevent multiple detections
  26. const float SUDDEN_CHANGE_FACTOR = 3.0; // Must be 3x normal motion
  27.  
  28. // Fall detection parameters
  29. const float FREE_FALL_THRESHOLD = 0.5; // Low G-force for free fall
  30. const float FALL_IMPACT_THRESHOLD = 4.0; // G-force for fall impact
  31. const unsigned long FALL_TIME_WINDOW = 3000; // Max time for fall sequence
  32. const unsigned long FALL_COOLDOWN = 5000; // Cooldown between fall detections
  33.  
  34. // State variables
  35. bool hitDetected = false;
  36. bool fallDetected = false;
  37. unsigned long lastHitTime = 0;
  38. unsigned long lastFallTime = 0;
  39. unsigned long impactStartTime = 0;
  40. bool analyzingImpact = false;
  41.  
  42. // Fall detection states
  43. bool inFreeFall = false;
  44. unsigned long fallStartTime = 0;
  45. bool waitingForFallImpact = false;
  46.  
  47. // For adaptive threshold calculation
  48. float normalAccelMax = 0;
  49. float normalGyroMax = 0;
  50. unsigned long lastNormalUpdate = 0;
  51. const unsigned long NORMAL_LEARNING_TIME = 5000; // Learn normal motion for 5 sec
  52.  
  53. // Impact analysis
  54. float impactAccelPeaks[10] = {0};
  55. float impactGyroPeaks[10] = {0};
  56. int peakCount = 0;
  57.  
  58. // Filtering
  59. const int MOVING_AVERAGE_SAMPLES = 5;
  60. float accelBuffer[MOVING_AVERAGE_SAMPLES] = {0};
  61. float gyroBuffer[MOVING_AVERAGE_SAMPLES] = {0};
  62. int bufferIndex = 0;
  63.  
  64. bool calibrated = false;
  65. const int CALIBRATION_SAMPLES = 100;
  66. bool learningPhase = true;
  67.  
  68. void setup() {
  69. Serial.begin(115200);
  70. Wire.begin();
  71.  
  72. // Wake up MPU6050
  73. Wire.beginTransmission(MPU_ADDR);
  74. Wire.write(PWR_MGMT_1);
  75. Wire.write(0);
  76. Wire.endTransmission(true);
  77.  
  78. Serial.println("HEAD IMPACT & FALL DETECTION SYSTEM");
  79. Serial.println("Learning normal motion for 5 seconds...");
  80. Serial.println("Please walk around normally");
  81. calibrateSensor();
  82. Serial.println("Calibration complete!");
  83. Serial.println("======================================");
  84. lastNormalUpdate = millis();
  85. }
  86.  
  87. void calibrateSensor() {
  88. float accelXSum = 0, accelYSum = 0, accelZSum = 0;
  89. float gyroXSum = 0, gyroYSum = 0, gyroZSum = 0;
  90.  
  91. for (int i = 0; i < CALIBRATION_SAMPLES; i++) {
  92. int16_t accelX = readSensor(ACCEL_XOUT_H);
  93. int16_t accelY = readSensor(ACCEL_XOUT_H + 2);
  94. int16_t accelZ = readSensor(ACCEL_XOUT_H + 4);
  95. int16_t gyroX = readSensor(GYRO_XOUT_H);
  96. int16_t gyroY = readSensor(GYRO_XOUT_H + 2);
  97. int16_t gyroZ = readSensor(GYRO_XOUT_H + 4);
  98.  
  99. accelXSum += accelX / 16384.0;
  100. accelYSum += accelY / 16384.0;
  101. accelZSum += accelZ / 16384.0;
  102. gyroXSum += gyroX / 131.0;
  103. gyroYSum += gyroY / 131.0;
  104. gyroZSum += gyroZ / 131.0;
  105.  
  106. delay(10);
  107. }
  108.  
  109. accelOffsetX = accelXSum / CALIBRATION_SAMPLES;
  110. accelOffsetY = accelYSum / CALIBRATION_SAMPLES;
  111. accelOffsetZ = accelZSum / CALIBRATION_SAMPLES - 1.0;
  112.  
  113. gyroOffsetX = gyroXSum / CALIBRATION_SAMPLES;
  114. gyroOffsetY = gyroYSum / CALIBRATION_SAMPLES;
  115. gyroOffsetZ = gyroZSum / CALIBRATION_SAMPLES;
  116.  
  117. calibrated = true;
  118. }
  119.  
  120. float movingAverage(float buffer[], float newValue) {
  121. buffer[bufferIndex] = newValue;
  122. bufferIndex = (bufferIndex + 1) % MOVING_AVERAGE_SAMPLES;
  123.  
  124. float sum = 0;
  125. for (int i = 0; i < MOVING_AVERAGE_SAMPLES; i++) {
  126. sum += buffer[i];
  127. }
  128. return sum / MOVING_AVERAGE_SAMPLES;
  129. }
  130.  
  131. void loop() {
  132. // Read sensor data
  133. int16_t rawAccelX = readSensor(ACCEL_XOUT_H);
  134. int16_t rawAccelY = readSensor(ACCEL_XOUT_H + 2);
  135. int16_t rawAccelZ = readSensor(ACCEL_XOUT_H + 4);
  136.  
  137. int16_t rawGyroX = readSensor(GYRO_XOUT_H);
  138. int16_t rawGyroY = readSensor(GYRO_XOUT_H + 2);
  139. int16_t rawGyroZ = readSensor(GYRO_XOUT_H + 4);
  140.  
  141. // Convert and apply calibration
  142. float accelX_g = rawAccelX / 16384.0 - accelOffsetX;
  143. float accelY_g = rawAccelY / 16384.0 - accelOffsetY;
  144. float accelZ_g = rawAccelZ / 16384.0 - accelOffsetZ;
  145.  
  146. float gyroX_deg = rawGyroX / 131.0 - gyroOffsetX;
  147. float gyroY_deg = rawGyroY / 131.0 - gyroOffsetY;
  148. float gyroZ_deg = rawGyroZ / 131.0 - gyroOffsetZ;
  149.  
  150. // Calculate magnitudes with moving average filtering
  151. float accelMagnitude = sqrt(accelX_g * accelX_g + accelY_g * accelY_g + accelZ_g * accelZ_g);
  152. float gyroMagnitude = sqrt(gyroX_deg * gyroX_deg + gyroY_deg * gyroY_deg + gyroZ_deg * gyroZ_deg);
  153.  
  154. float filteredAccel = movingAverage(accelBuffer, accelMagnitude);
  155. float filteredGyro = movingAverage(gyroBuffer, gyroMagnitude);
  156.  
  157. unsigned long currentTime = millis();
  158.  
  159. // Learning phase - understand normal motion
  160. if (learningPhase) {
  161. updateNormalMotionLevels(filteredAccel, filteredGyro, currentTime);
  162. }
  163.  
  164. // Run both detection systems
  165. checkForImpact(filteredAccel, filteredGyro, currentTime);
  166. checkForFall(filteredAccel, currentTime);
  167.  
  168. // Print status
  169. static unsigned long lastPrint = 0;
  170. if (currentTime - lastPrint > 1000) {
  171. printStatus(accelX_g, accelY_g, accelZ_g, gyroX_deg, gyroY_deg, gyroZ_deg,
  172. filteredAccel, filteredGyro, currentTime);
  173. lastPrint = currentTime;
  174. }
  175.  
  176. delay(20);
  177. }
  178.  
  179. void updateNormalMotionLevels(float accel, float gyro, unsigned long currentTime) {
  180. // Update maximum normal motion levels during learning phase
  181. if (accel > normalAccelMax) normalAccelMax = accel;
  182. if (gyro > normalGyroMax) normalGyroMax = gyro;
  183.  
  184. // End learning phase after 5 seconds
  185. if (currentTime - lastNormalUpdate > NORMAL_LEARNING_TIME) {
  186. learningPhase = false;
  187.  
  188. // Set adaptive thresholds based on learned normal motion
  189. adaptiveAccelThreshold = normalAccelMax * SUDDEN_CHANGE_FACTOR;
  190. adaptiveGyroThreshold = normalGyroMax * SUDDEN_CHANGE_FACTOR;
  191.  
  192. // Ensure minimum thresholds
  193. if (adaptiveAccelThreshold < 2.0) adaptiveAccelThreshold = 2.0;
  194. if (adaptiveGyroThreshold < 30.0) adaptiveGyroThreshold = 30.0;
  195.  
  196. Serial.println("\n*** LEARNING PHASE COMPLETE ***");
  197. Serial.print("Normal motion - Max Accel: "); Serial.print(normalAccelMax, 2); Serial.println("g");
  198. Serial.print("Normal motion - Max Gyro: "); Serial.print(normalGyroMax, 1); Serial.println("Β°/s");
  199. Serial.print("Adaptive thresholds - Accel: "); Serial.print(adaptiveAccelThreshold, 2); Serial.println("g");
  200. Serial.print("Adaptive thresholds - Gyro: "); Serial.print(adaptiveGyroThreshold, 1); Serial.println("Β°/s");
  201. Serial.println("Now monitoring for impacts and falls...");
  202. }
  203. }
  204.  
  205. void checkForImpact(float accel, float gyro, unsigned long currentTime) {
  206. // Check if we have a sudden change that might be an impact
  207. bool suddenChange = (accel > adaptiveAccelThreshold) || (gyro > adaptiveGyroThreshold);
  208.  
  209. if (suddenChange && !analyzingImpact) {
  210. // Start analyzing potential impact
  211. analyzingImpact = true;
  212. impactStartTime = currentTime;
  213. peakCount = 0;
  214. impactAccelPeaks[0] = accel;
  215. impactGyroPeaks[0] = gyro;
  216. Serial.println("πŸ’₯ Potential impact detected - analyzing...");
  217. }
  218.  
  219. if (analyzingImpact) {
  220. // Track peaks during impact window
  221. if (accel > impactAccelPeaks[peakCount]) impactAccelPeaks[peakCount] = accel;
  222. if (gyro > impactGyroPeaks[peakCount]) impactGyroPeaks[peakCount] = gyro;
  223.  
  224. // Check if impact analysis period is over
  225. if (currentTime - impactStartTime > IMPACT_WINDOW) {
  226. analyzeImpactPattern(currentTime);
  227. analyzingImpact = false;
  228. }
  229.  
  230. // Move to next peak every 20ms
  231. if (currentTime - impactStartTime > (peakCount + 1) * 20 && peakCount < 9) {
  232. peakCount++;
  233. impactAccelPeaks[peakCount] = accel;
  234. impactGyroPeaks[peakCount] = gyro;
  235. }
  236. }
  237.  
  238. // Reset hit detection after cooldown
  239. if (currentTime - lastHitTime > HIT_COOLDOWN) {
  240. hitDetected = false;
  241. }
  242. }
  243.  
  244. void analyzeImpactPattern(unsigned long currentTime) {
  245. // Find maximum values during impact window
  246. float maxAccel = 0;
  247. float maxGyro = 0;
  248.  
  249. for (int i = 0; i <= peakCount; i++) {
  250. if (impactAccelPeaks[i] > maxAccel) maxAccel = impactAccelPeaks[i];
  251. if (impactGyroPeaks[i] > maxGyro) maxGyro = impactGyroPeaks[i];
  252. }
  253.  
  254. // Determine if this is a real head impact
  255. bool isHeadImpact = verifyHeadImpact(maxAccel, maxGyro);
  256.  
  257. if (isHeadImpact && !hitDetected) {
  258. hitDetected = true;
  259. lastHitTime = currentTime;
  260.  
  261. Serial.println("🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨");
  262. Serial.println("🚨 HEAD IMPACT DETECTED! 🚨");
  263. Serial.println("🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨");
  264. Serial.print("Impact Force: "); Serial.print(maxAccel, 1); Serial.println("g");
  265. Serial.print("Rotational Force: "); Serial.print(maxGyro, 1); Serial.println("Β°/s");
  266. Serial.print("Thresholds: "); Serial.print(adaptiveAccelThreshold, 1);
  267. Serial.print("g / "); Serial.print(adaptiveGyroThreshold, 1); Serial.println("Β°/s");
  268. Serial.println("⚠️ CHECK USER FOR CONCUSSION SYMPTOMS!");
  269. Serial.println();
  270. } else if (!isHeadImpact) {
  271. Serial.print("❌ False alarm: ");
  272. Serial.print(maxAccel, 1); Serial.print("g, ");
  273. Serial.print(maxGyro, 1); Serial.println("Β°/s - Normal motion pattern");
  274. }
  275. }
  276.  
  277. bool verifyHeadImpact(float maxAccel, float maxGyro) {
  278. // Real head impacts typically have:
  279. // 1. Very high acceleration OR very high rotation
  280. // 2. Both values significantly above normal motion
  281. // 3. Specific pattern characteristics
  282.  
  283. bool highAccel = maxAccel > (normalAccelMax * 4.0); // 4x normal motion
  284. bool highGyro = maxGyro > (normalGyroMax * 4.0); // 4x normal motion
  285.  
  286. // Head impact usually has both linear and rotational components
  287. bool hasBothComponents = (maxAccel > adaptiveAccelThreshold * 1.2) &&
  288. (maxGyro > adaptiveGyroThreshold * 1.2);
  289.  
  290. // Or extremely high values in one component
  291. bool extremeSingleComponent = (maxAccel > adaptiveAccelThreshold * 2.0) ||
  292. (maxGyro > adaptiveGyroThreshold * 2.0);
  293.  
  294. return (hasBothComponents || extremeSingleComponent) && (highAccel || highGyro);
  295. }
  296.  
  297. void checkForFall(float accel, unsigned long currentTime) {
  298. // Fall detection logic: Free fall followed by impact
  299.  
  300. // Step 1: Detect free fall (low acceleration)
  301. if (accel < FREE_FALL_THRESHOLD && !inFreeFall && !waitingForFallImpact) {
  302. inFreeFall = true;
  303. fallStartTime = currentTime;
  304. Serial.println("⚠️ Free fall detected! Monitoring for impact...");
  305. }
  306.  
  307. // Step 2: If we're in free fall, wait for impact
  308. if (inFreeFall) {
  309. // Check if free fall period is too long (might not be a real fall)
  310. if (currentTime - fallStartTime > FALL_TIME_WINDOW) {
  311. inFreeFall = false;
  312. Serial.println("❌ Free fall too long - probably not a real fall");
  313. }
  314.  
  315. // Check for impact after free fall
  316. if (accel > FALL_IMPACT_THRESHOLD) {
  317. // This is the impact after a fall
  318. if (!fallDetected && (currentTime - lastFallTime > FALL_COOLDOWN)) {
  319. fallDetected = true;
  320. lastFallTime = currentTime;
  321. inFreeFall = false;
  322. waitingForFallImpact = false;
  323.  
  324. Serial.println("🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨");
  325. Serial.println("🚨 FALL DETECTED! 🚨");
  326. Serial.println("🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨 🚨");
  327. Serial.print("Fall Impact: "); Serial.print(accel, 1); Serial.println("g");
  328. Serial.print("Free fall duration: ");
  329. Serial.print(currentTime - fallStartTime);
  330. Serial.println("ms");
  331. Serial.println("⚠️ USER MAY HAVE FALLEN - CHECK IMMEDIATELY!");
  332. Serial.println();
  333. }
  334. }
  335. }
  336.  
  337. // Reset fall detection after cooldown
  338. if (currentTime - lastFallTime > FALL_COOLDOWN) {
  339. fallDetected = false;
  340. }
  341.  
  342. // Reset free fall state if acceleration returns to normal
  343. if (inFreeFall && accel > FREE_FALL_THRESHOLD * 2.0) {
  344. inFreeFall = false;
  345. waitingForFallImpact = true;
  346. // Wait a bit to see if impact comes
  347. if (currentTime - fallStartTime > 1000) {
  348. waitingForFallImpact = false;
  349. }
  350. }
  351. }
  352.  
  353. void printStatus(float accelX, float accelY, float accelZ, float gyroX, float gyroY, float gyroZ,
  354. float accelMag, float gyroMag, unsigned long currentTime) {
  355. Serial.print("Accel: ");
  356. Serial.print(accelX, 2); Serial.print("g, ");
  357. Serial.print(accelY, 2); Serial.print("g, ");
  358. Serial.print(accelZ, 2); Serial.print("g");
  359. Serial.print(" | Total: "); Serial.print(accelMag, 2); Serial.println("g");
  360.  
  361. Serial.print("Gyro: ");
  362. Serial.print(gyroX, 1); Serial.print("Β°/s, ");
  363. Serial.print(gyroY, 1); Serial.print("Β°/s, ");
  364. Serial.print(gyroZ, 1); Serial.print("Β°/s");
  365. Serial.print(" | Total: "); Serial.print(gyroMag, 1); Serial.println("Β°/s");
  366.  
  367. if (learningPhase) {
  368. float remaining = (NORMAL_LEARNING_TIME - (currentTime - lastNormalUpdate)) / 1000.0;
  369. Serial.print("πŸ“š Learning normal motion: ");
  370. Serial.print(remaining, 0); Serial.println("s remaining");
  371. Serial.print("Current max - Accel: "); Serial.print(normalAccelMax, 2);
  372. Serial.print("g, Gyro: "); Serial.print(normalGyroMax, 1); Serial.println("Β°/s");
  373. } else {
  374. Serial.print("🎯 Thresholds: "); Serial.print(adaptiveAccelThreshold, 1);
  375. Serial.print("g / "); Serial.print(adaptiveGyroThreshold, 1); Serial.println("Β°/s");
  376.  
  377. // Status display for both systems
  378. if (hitDetected) {
  379. Serial.println("🚨 HEAD IMPACT DETECTED - CHECK USER!");
  380. } else if (fallDetected) {
  381. Serial.println("🚨 FALL DETECTED - USER MAY HAVE FALLEN!");
  382. } else if (inFreeFall) {
  383. Serial.println("⚠️ Free fall detected - watching for impact...");
  384. } else if (analyzingImpact) {
  385. Serial.println("πŸ’₯ Analyzing potential impact...");
  386. } else {
  387. Serial.println("βœ… Normal - Monitoring impacts & falls");
  388. }
  389. }
  390.  
  391. Serial.println("-----------------------");
  392. }
  393.  
  394. int16_t readSensor(uint8_t reg) {
  395. Wire.beginTransmission(MPU_ADDR);
  396. Wire.write(reg);
  397. Wire.endTransmission(false);
  398. Wire.requestFrom(MPU_ADDR, 2, true);
  399. return (Wire.read() << 8) | Wire.read();
  400. }
Advertisement
Add Comment
Please, Sign In to add comment