xsns_32_mpu6050.ino 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. xsns_32_mpu6050.ino - MPU6050 gyroscope and temperature sensor support for Sonoff-Tasmota
  3. Copyright (C) 2018 Oliver Welter
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_I2C
  16. #ifdef USE_MPU6050
  17. /*********************************************************************************************\
  18. * MPU6050 3 axis gyroscope and temperature sensor
  19. *
  20. * Source: Oliver Welter, with special thanks to Jeff Rowberg
  21. *
  22. * I2C Address: 0x68 or 0x69 with AD0 HIGH
  23. \*********************************************************************************************/
  24. #define XSNS_32 32
  25. #define D_SENSOR_MPU6050 "MPU6050"
  26. #define MPU_6050_ADDR_AD0_LOW 0x68
  27. #define MPU_6050_ADDR_AD0_HIGH 0x69
  28. uint8_t MPU_6050_address;
  29. uint8_t MPU_6050_addresses[] = { MPU_6050_ADDR_AD0_LOW, MPU_6050_ADDR_AD0_HIGH };
  30. uint8_t MPU_6050_found;
  31. int16_t MPU_6050_ax = 0, MPU_6050_ay = 0, MPU_6050_az = 0;
  32. int16_t MPU_6050_gx = 0, MPU_6050_gy = 0, MPU_6050_gz = 0;
  33. int16_t MPU_6050_temperature = 0;
  34. #ifdef USE_MPU6050_DMP
  35. #include "MPU6050_6Axis_MotionApps20.h"
  36. #include "I2Cdev.h"
  37. #include <helper_3dmath.h>
  38. typedef struct MPU6050_DMP{
  39. uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
  40. uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
  41. uint16_t fifoCount; // count of all bytes currently in FIFO
  42. uint8_t fifoBuffer[64]; // FIFO storage buffer
  43. Quaternion q; // [w, x, y, z] quaternion container
  44. VectorInt16 aa; // [x, y, z] accel sensor measurements
  45. VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements
  46. VectorFloat gravity; // [x, y, z] gravity vector
  47. float euler[3]; // [psi, theta, phi] Euler angle container
  48. } MPU6050_DMP;
  49. MPU6050_DMP MPU6050_dmp;
  50. #else
  51. #include <MPU6050.h>
  52. #endif //USE_MPU6050_DMP
  53. MPU6050 mpu6050;
  54. void MPU_6050PerformReading(void)
  55. {
  56. #ifdef USE_MPU6050_DMP
  57. mpu6050.resetFIFO(); // with a default dampling rate of 200Hz, we create a delay of approx. 5ms with a complete read cycle
  58. MPU6050_dmp.fifoCount = mpu6050.getFIFOCount();
  59. while (MPU6050_dmp.fifoCount < MPU6050_dmp.packetSize) MPU6050_dmp.fifoCount = mpu6050.getFIFOCount();
  60. mpu6050.getFIFOBytes(MPU6050_dmp.fifoBuffer, MPU6050_dmp.packetSize);
  61. MPU6050_dmp.fifoCount -= MPU6050_dmp.packetSize;
  62. // calculate euler and acceleration with the DMP
  63. mpu6050.dmpGetQuaternion(&MPU6050_dmp.q, MPU6050_dmp.fifoBuffer);
  64. mpu6050.dmpGetEuler(MPU6050_dmp.euler, &MPU6050_dmp.q);
  65. mpu6050.dmpGetAccel(&MPU6050_dmp.aa, MPU6050_dmp.fifoBuffer);
  66. mpu6050.dmpGetGravity(&MPU6050_dmp.gravity, &MPU6050_dmp.q);
  67. mpu6050.dmpGetLinearAccel(&MPU6050_dmp.aaReal, &MPU6050_dmp.aa, &MPU6050_dmp.gravity);
  68. MPU_6050_gx = MPU6050_dmp.euler[0] * 180/M_PI;
  69. MPU_6050_gy = MPU6050_dmp.euler[1] * 180/M_PI;
  70. MPU_6050_gz = MPU6050_dmp.euler[2] * 180/M_PI;
  71. MPU_6050_ax = MPU6050_dmp.aaReal.x;
  72. MPU_6050_ay = MPU6050_dmp.aaReal.y;
  73. MPU_6050_az = MPU6050_dmp.aaReal.z;
  74. #else
  75. mpu6050.getMotion6(
  76. &MPU_6050_ax,
  77. &MPU_6050_ay,
  78. &MPU_6050_az,
  79. &MPU_6050_gx,
  80. &MPU_6050_gy,
  81. &MPU_6050_gz
  82. );
  83. #endif //USE_MPU6050_DMP
  84. MPU_6050_temperature = mpu6050.getTemperature();
  85. }
  86. /* Work in progress - not yet fully functional
  87. void MPU_6050SetGyroOffsets(int x, int y, int z)
  88. {
  89. mpu050.setXGyroOffset(x);
  90. mpu6050.setYGyroOffset(y);
  91. mpu6050.setZGyroOffset(z);
  92. }
  93. void MPU_6050SetAccelOffsets(int x, int y, int z)
  94. {
  95. mpu6050.setXAccelOffset(x);
  96. mpu6050.setYAccelOffset(y);
  97. mpu6050.setZAccelOffset(z);
  98. }
  99. */
  100. void MPU_6050Detect(void)
  101. {
  102. if (MPU_6050_found)
  103. {
  104. return;
  105. }
  106. for (byte i = 0; i < sizeof(MPU_6050_addresses); i++)
  107. {
  108. if(!I2cDevice(MPU_6050_addresses[i]))
  109. {
  110. break;
  111. }
  112. MPU_6050_address = MPU_6050_addresses[i];
  113. mpu6050.setAddr(MPU_6050_addresses[i]);
  114. #ifdef USE_MPU6050_DMP
  115. MPU6050_dmp.devStatus = mpu6050.dmpInitialize();
  116. mpu6050.setXGyroOffset(220);
  117. mpu6050.setYGyroOffset(76);
  118. mpu6050.setZGyroOffset(-85);
  119. mpu6050.setZAccelOffset(1788);
  120. if (MPU6050_dmp.devStatus == 0) {
  121. mpu6050.setDMPEnabled(true);
  122. MPU6050_dmp.packetSize = mpu6050.dmpGetFIFOPacketSize();
  123. MPU_6050_found = true;
  124. }
  125. #else
  126. mpu6050.initialize();
  127. MPU_6050_found = mpu6050.testConnection();
  128. #endif //USE_MPU6050_DMP
  129. Settings.flag2.axis_resolution = 2; // Need to be services by command Sensor32
  130. }
  131. if (MPU_6050_found)
  132. {
  133. snprintf_P(log_data, sizeof(log_data), S_LOG_I2C_FOUND_AT, D_SENSOR_MPU6050, MPU_6050_address);
  134. AddLog(LOG_LEVEL_DEBUG);
  135. }
  136. }
  137. #ifdef USE_WEBSERVER
  138. const char HTTP_SNS_AX_AXIS[] PROGMEM = "%s{s}%s " D_AX_AXIS "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
  139. const char HTTP_SNS_AY_AXIS[] PROGMEM = "%s{s}%s " D_AY_AXIS "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
  140. const char HTTP_SNS_AZ_AXIS[] PROGMEM = "%s{s}%s " D_AZ_AXIS "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
  141. const char HTTP_SNS_GX_AXIS[] PROGMEM = "%s{s}%s " D_GX_AXIS "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
  142. const char HTTP_SNS_GY_AXIS[] PROGMEM = "%s{s}%s " D_GY_AXIS "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
  143. const char HTTP_SNS_GZ_AXIS[] PROGMEM = "%s{s}%s " D_GZ_AXIS "{m}%s{e}"; // {s} = <tr><th>, {m} = </th><td>, {e} = </td></tr>
  144. #endif // USE_WEBSERVER
  145. #define D_JSON_AXIS_AX "AccelXAxis"
  146. #define D_JSON_AXIS_AY "AccelYAxis"
  147. #define D_JSON_AXIS_AZ "AccelZAxis"
  148. #define D_JSON_AXIS_GX "GyroXAxis"
  149. #define D_JSON_AXIS_GY "GyroYAxis"
  150. #define D_JSON_AXIS_GZ "GyroZAxis"
  151. void MPU_6050Show(boolean json)
  152. {
  153. if (MPU_6050_found) {
  154. MPU_6050PerformReading();
  155. double tempConv = (MPU_6050_temperature / 340.0 + 35.53);
  156. char temperature[33];
  157. dtostrfd(tempConv, Settings.flag2.temperature_resolution, temperature);
  158. char axis_ax[33];
  159. dtostrfd(MPU_6050_ax, Settings.flag2.axis_resolution, axis_ax);
  160. char axis_ay[33];
  161. dtostrfd(MPU_6050_ay, Settings.flag2.axis_resolution, axis_ay);
  162. char axis_az[33];
  163. dtostrfd(MPU_6050_az, Settings.flag2.axis_resolution, axis_az);
  164. char axis_gx[33];
  165. dtostrfd(MPU_6050_gx, Settings.flag2.axis_resolution, axis_gx);
  166. char axis_gy[33];
  167. dtostrfd(MPU_6050_gy, Settings.flag2.axis_resolution, axis_gy);
  168. char axis_gz[33];
  169. dtostrfd(MPU_6050_gz, Settings.flag2.axis_resolution, axis_gz);
  170. if (json) {
  171. char json_axis_ax[25];
  172. snprintf_P(json_axis_ax, sizeof(json_axis_ax), PSTR(",\"" D_JSON_AXIS_AX "\":%s"), axis_ax);
  173. char json_axis_ay[25];
  174. snprintf_P(json_axis_ay, sizeof(json_axis_ay), PSTR(",\"" D_JSON_AXIS_AY "\":%s"), axis_ay);
  175. char json_axis_az[25];
  176. snprintf_P(json_axis_az, sizeof(json_axis_az), PSTR(",\"" D_JSON_AXIS_AZ "\":%s"), axis_az);
  177. char json_axis_gx[25];
  178. snprintf_P(json_axis_gx, sizeof(json_axis_gx), PSTR(",\"" D_JSON_AXIS_GX "\":%s"), axis_gx);
  179. char json_axis_gy[25];
  180. snprintf_P(json_axis_gy, sizeof(json_axis_gy), PSTR(",\"" D_JSON_AXIS_GY "\":%s"), axis_gy);
  181. char json_axis_gz[25];
  182. snprintf_P(json_axis_gz, sizeof(json_axis_gz), PSTR(",\"" D_JSON_AXIS_GZ "\":%s"), axis_gz);
  183. snprintf_P(mqtt_data, sizeof(mqtt_data), PSTR("%s,\"%s\":{\"" D_JSON_TEMPERATURE "\":%s%s%s%s%s%s%s}"),
  184. mqtt_data, D_SENSOR_MPU6050, temperature, json_axis_ax, json_axis_ay, json_axis_az, json_axis_gx, json_axis_gy, json_axis_gz);
  185. #ifdef USE_DOMOTICZ
  186. DomoticzSensor(DZ_TEMP, temperature);
  187. #endif // USE_DOMOTICZ
  188. #ifdef USE_WEBSERVER
  189. } else {
  190. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_TEMP, mqtt_data, D_SENSOR_MPU6050, temperature, TempUnit());
  191. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_AX_AXIS, mqtt_data, D_SENSOR_MPU6050, axis_ax);
  192. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_AY_AXIS, mqtt_data, D_SENSOR_MPU6050, axis_ay);
  193. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_AZ_AXIS, mqtt_data, D_SENSOR_MPU6050, axis_az);
  194. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_GX_AXIS, mqtt_data, D_SENSOR_MPU6050, axis_gx);
  195. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_GY_AXIS, mqtt_data, D_SENSOR_MPU6050, axis_gy);
  196. snprintf_P(mqtt_data, sizeof(mqtt_data), HTTP_SNS_GZ_AXIS, mqtt_data, D_SENSOR_MPU6050, axis_gz);
  197. #endif // USE_WEBSERVER
  198. }
  199. }
  200. }
  201. /*********************************************************************************************\
  202. * Interface
  203. \*********************************************************************************************/
  204. boolean Xsns32(byte function)
  205. {
  206. boolean result = false;
  207. if (i2c_flg) {
  208. switch (function) {
  209. case FUNC_PREP_BEFORE_TELEPERIOD:
  210. MPU_6050Detect();
  211. break;
  212. case FUNC_EVERY_SECOND:
  213. if (tele_period == Settings.tele_period -3) {
  214. MPU_6050PerformReading();
  215. }
  216. break;
  217. case FUNC_JSON_APPEND:
  218. MPU_6050Show(1);
  219. break;
  220. #ifdef USE_WEBSERVER
  221. case FUNC_WEB_APPEND:
  222. MPU_6050Show(0);
  223. MPU_6050PerformReading();
  224. break;
  225. #endif // USE_WEBSERVER
  226. }
  227. }
  228. return result;
  229. }
  230. #endif // USE_MPU6050
  231. #endif // USE_I2C