├── .gitignore ├── .travis.yml ├── .vscode ├── extensions.json └── settings.json ├── CMakeLists.txt ├── README.md ├── data ├── bundle.js └── index.html ├── include └── README ├── lib ├── BmeHelper │ ├── BmeHelper.cpp │ ├── BmeHelper.h │ └── library.json ├── BsecHelper │ ├── BsecHelper.cpp │ ├── BsecHelper.h │ └── library.json ├── Co2Helper │ ├── Co2Helper.cpp │ ├── Co2Helper.h │ └── library.json ├── I2Cdev │ ├── I2Cdev.cpp │ ├── I2Cdev.h │ ├── keywords.txt │ └── library.json ├── KnobHelper │ ├── KnobHelper.cpp │ ├── KnobHelper.h │ └── library.json ├── LedHelper │ ├── LedHelper.cpp │ ├── LedHelper.h │ └── library.json ├── MPU6050 │ ├── MPU6050.cpp │ ├── MPU6050.h │ └── library.json ├── MpuHelper │ ├── MpuHelper.cpp │ ├── MpuHelper.h │ └── library.json ├── WaveshareHelper │ ├── WaveshareHelper.cpp │ ├── WaveshareHelper.h │ └── library.json ├── WebServerHelper │ ├── WebServerHelper.cpp │ ├── WebServerHelper.h │ └── library.json └── WiFiHelper │ ├── WiFiHelper.cpp │ ├── WiFiHelper.h │ └── library.json ├── partitions_custom.csv ├── platformio.ini ├── screenshots ├── aqiLed_wiring.png ├── aqiled.png ├── co2.png ├── co2_wiring.png ├── cube.png ├── cube_wiring.png ├── display.png ├── display_wiring.png ├── firmware.png ├── knob.png ├── knob_wiring.png ├── mpu6050.png ├── sleep.png ├── updateWebsite.png ├── upload.png ├── website.png └── wifi.png ├── src ├── CMakeLists.txt ├── aqiLed.cpp ├── co2.cpp ├── cube.cpp ├── display.cpp └── knob.cpp └── test └── README /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < https://docs.platformio.org/page/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < https://docs.platformio.org/page/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < https://docs.platformio.org/page/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choose one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # - platformio update 39 | # 40 | # script: 41 | # - platformio run 42 | 43 | 44 | # 45 | # Template #2: The project is intended to be used as a library with examples. 46 | # 47 | 48 | # language: python 49 | # python: 50 | # - "2.7" 51 | # 52 | # sudo: false 53 | # cache: 54 | # directories: 55 | # - "~/.platformio" 56 | # 57 | # env: 58 | # - PLATFORMIO_CI_SRC=path/to/test/file.c 59 | # - PLATFORMIO_CI_SRC=examples/file.ino 60 | # - PLATFORMIO_CI_SRC=path/to/test/directory 61 | # 62 | # install: 63 | # - pip install -U platformio 64 | # - platformio update 65 | # 66 | # script: 67 | # - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N 68 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "functional": "cpp", 4 | "array": "cpp", 5 | "deque": "cpp", 6 | "string": "cpp", 7 | "unordered_map": "cpp", 8 | "unordered_set": "cpp", 9 | "vector": "cpp", 10 | "initializer_list": "cpp", 11 | "regex": "cpp", 12 | "*.tcc": "cpp", 13 | "bitset": "cpp", 14 | "fstream": "cpp", 15 | "istream": "cpp", 16 | "ostream": "cpp", 17 | "sstream": "cpp", 18 | "streambuf": "cpp", 19 | "system_error": "cpp", 20 | "tuple": "cpp", 21 | "iosfwd": "cpp", 22 | "limits": "cpp", 23 | "cstdlib": "cpp", 24 | "*.txt": "cpp", 25 | "random": "cpp", 26 | "type_traits": "cpp", 27 | "utility": "cpp" 28 | }, 29 | "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: Google, IndentWidth: 2, ColumnLimit: 0 }", 30 | "editor.formatOnSave": true 31 | } 32 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | project(espbs) 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # espbs 2 | 3 | Collection of my ESP32 projects.
4 | 5 | The project is set up with [platformIO](https://platformio.org/), and I'm using VS-Code as a code editor.
6 | https://randomnerdtutorials.com/vs-code-platformio-ide-esp32-esp8266-arduino/#1a 7 | 8 | To reuse and maintain the base libraries, I decided to create one single project for different applications, switchable in the platform.ini. 9 | 10 | The bundled configuration website is already part of this project (data) and stored on a separate spiffs partition on the ESP. If you want to adjust the website, please see [espjs project](https://github.com/Lillifee/espjs)
11 | 12 | Please let me know if you have ideas, improvements, or problems during setup.
I'm also interested in your projects and willing to add them to the repository.
If you have fancy 3d prints for an application, send me a message, and I will add them to the readme. 13 | 14 | ## Switch application 15 | 16 | To switch the application from e.g. cube to knob 17 | 18 | - open the platformio.ini file 19 | - check the [board](https://docs.platformio.org/en/latest/boards/index.html) type (lolin_d32, lolin32, wemos_d1_mini32 etc.) 20 | - comment the cube section 21 | - uncomment the knob section 22 | 23 | ## Upload the project 24 | 25 | You can eigher run the commands in the platformIO terminal or just use the PlatformIO tab in VSCode 26 | 27 | ### `pio run -t upload` - Build and upload the firmware 28 | 29 | ### `pio run -t uploadfs` - Build and upload the website partition 30 | 31 |
32 | 33 | ![picture](screenshots/upload.png) 34 | 35 | - `Upload` - build and upload the firmware (firmware.bin) 36 | - `Upload Filesystem Image` - Upload the website partition (spiffs.bin) 37 | 38 | ## Setup 39 | 40 | On the first startup, the ESP creates an access point called "espbs" to configure your Wi-Fi settings. 41 |
Connect to the WLAN `espbs` with password `password`. Open the browser with http://192.168.4.1, and add your Wi-Fi settings. 42 | 43 | ![picture](screenshots/website.png) 44 | 45 | I would also recommend using a static IP address to improve the startup time of the ESP. 46 |
You can also assign a host name. This set the MDNS host name and allows you to connect to your ESP with `http://{yourHostName}.local` in your network. 47 | 48 | ![picture](screenshots/wifi.png) 49 | 50 | After applying the changes, the ESP will reboot and connect to your network. 51 | 52 | ![picture](screenshots/sleep.png) Some applications are running in deep-sleep to reduce power consumption. 53 | If you finished the configuraiton, don't forget to send the esp to deep sleep with the sleep button in the right bottom corner of the "Update" section. 54 | 55 | To change the settings while the esp is in sleep mode, you can press the RESET button on the ESP to start the webserver again. 56 | 57 | The reset button will not affect your settings, so if you misconfigured your ESP (e.g. wrong Wi-Fi settings) you can erase the flash from the PlatformIO menu and start from scratch. 58 | 59 | ## Update firmware 60 | 61 | Once the espbs is running, it's possible to update the firmware over the web interface.
62 | This can be quite handy if you want to update several devices on your network. e.g. multiple CO2 sensors. 63 | 64 | - Navigate to the platformIO section in VS Code and press `Build` 65 | - If you have done changes on the website (data) use `Build Filesystem Image` 66 | - Once the build is finished you should find a firmware.bin and a spiffs.bin (website) in your output folder /.pio/build/{board_name}/ 67 | 68 | Open the website and upload the new firmware. 69 | 70 | - ![picture](screenshots/firmware.png) Press the update firmware button and choose the firmware.bin file. 71 | - ![picture](screenshots/updateWebsite.png) Press the update website button and choose the spiffs.bin file. 72 | 73 | # Applications 74 | 75 | # Cube 76 | 77 | The cube is a remote control for 6 different functions. It uses an MPU6050 to figure out which side is on top and send a UDP request into your network. To save the battery life, the ESP32 remains in a deep sleep until the cube gets rotated. I switch four different light scenes in my living room and control the blinds with the remaining sides. 78 | 79 | I made this cube out of a parquet floor because I had no 3d printer back then. Send me your design's and I will add a link here. 80 | 81 | ![picture](screenshots/cube.png) 82 | 83 | ### Hardware 84 | 85 | - MPU6050 - GY512 86 | - ESP LOLIN32 87 | - 3,7V 1100mAh Lithium Akku 88 | 89 | ![picture](screenshots/cube_wiring.png) 90 | 91 | INT - is used to wake up from deep sleep. Feel free to use another pin and change it in the ./src/cube.cpp 92 | esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); 93 | 94 | ### Optimization 95 | 96 | I also found a nice article about optimizing for power consumption:
97 | https://bitbanksoftware.blogspot.com/2018/10/how-low-can-it-go-optimizing-for-power.html 98 | 99 | 1. Remove the voltage regulator and bridge it. 100 | 2. Remove the LED 101 | 102 | ![picture](screenshots/mpu6050.png) 103 | 104 | # CO2 105 | 106 | The co2 sends the air quality as a UDP request to your home automation. I added two separate sensors to measure air quality in my living room, the MHZ-19 for the CO2 measurements, and a Bosh BME680 for the overall air quality. This application doesn't use deep sleep, and the website is running all the time. Therefore it's not necessary to send the ESP to deep sleep after configuration. 107 | 108 | You can find the 3d printing files on https://www.prusaprinters.org/prints/47612-air-quality-sensor-case-esp32 109 | 110 | ![picture](screenshots/co2.png) 111 | 112 | ### BSEC - Bosch Sensortec 113 | 114 | To run the BSECHelper with BME680 for additional IAQ (indoor air quality) - check the BSEC-Arduino-library. 115 | The library is not included in this repo. 116 | https://github.com/BoschSensortec/BSEC-Arduino-library 117 | 118 | Copy the library into ./pio/libdeps/BSECSoftwareLibrary and enable the special build flags in the platformio.ini 119 | 120 | ### Hardware 121 | 122 | - BME680 - pimoroni 123 | - MH-Z19 CO2 sensor 124 | - ESP LOLIN32 125 | 126 | ![picture](screenshots/co2_wiring.png) 127 | 128 | # Display 129 | 130 | The display uses a waveshare 7.5 inch display to visualize the values of your smart home. I use it to visualize the power consumption and the air quality in the living room. To save the battery life, the ESP32 remains in deep sleep and updates in the configured interval. At the moment, the displayed values are not configurable, but you can adjust it to your needs in the lib/WaveshareHelper library. 131 | 132 | You can find the 3d printing files on https://www.prusaprinters.org/prints/47605-e-paper-display-stand-esp32 133 | 134 | ![picture](screenshots/display.png) 135 | 136 | ### Hardware 137 | 138 | - ESP LOLIN32 139 | - Waveshare 7.5 inch e-paper display 140 | - 3,7V 1100mAh Lithium Akku 141 | 142 | ![picture](screenshots/display_wiring.png) 143 | 144 | # Knob 145 | 146 | The knob uses a rotary encoder to e.g. dim the light over Wi-Fi. 147 | To save the battery life, the ESP32 remains in a deep sleep until the knob gets pressed. On rotation, the current value is sent as a UDP packet into the network to my home automation server. After a few seconds without value change, the ESP goes back to deep sleep. 148 | 149 | You can find the 3d printing files on https://www.prusaprinters.org/prints/47614-wifi-knob-esp32 150 | 151 | ![picture](screenshots/knob.png) 152 | 153 | ### Hardware 154 | 155 | - ESP LOLIN32 lite 156 | - Rotary encoder 157 | - 3,7V 1100mAh Lithium Akku 158 | 159 | ![picture](screenshots/knob_wiring.png) 160 | 161 | Feel free to change the wiring in ./lib/KnobHelper/KnobHelper.h and the wakeup pin in ./src/knob.cpp 162 | 163 | # Air quality lamp 164 | 165 | The AQI-Led uses a BME680 and a LED ring (WS2812) to visualize the IAQ (Indoor air quality). 166 | There are different modes to change the visualization on the website. 167 | 168 | - The rainbow mode uses individual LEDs like a progress bar. 0 LEDs (excellent) to 24 LEDs (bad). 169 | - The color change mode uses all LEDs and indicates the IAQ as a color. Green (excellent) to red (bad) 170 | 171 | Besides the brightness, minimum value, there are also some animations to create ambient light. 172 | 173 | You can find the 3d printing files on https://www.prusaprinters.org/prints/49874-air-quality-lamp 174 | 175 | ![picture](screenshots/aqiled.png) 176 | 177 | ### Hardware 178 | 179 | - ESP LOLIN32 180 | - BME680 (BlueDot) 181 | - LED ring (WS2812) 182 | 183 | ![picture](screenshots/aqiLed_wiring.png) 184 | -------------------------------------------------------------------------------- /data/index.html: -------------------------------------------------------------------------------- 1 | ESP
-------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /lib/BmeHelper/BmeHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "BmeHelper.h" 2 | 3 | void BmeHelperClass::read() { 4 | preferences.begin("bme", true); 5 | host = preferences.getString("host"); 6 | port = preferences.getInt("port", 3333); 7 | requestInterval = preferences.getInt("reqInterval", 60000); 8 | preferences.end(); 9 | } 10 | 11 | void BmeHelperClass::write() { 12 | preferences.begin("bme", false); 13 | preferences.putString("host", host); 14 | preferences.putInt("port", port); 15 | preferences.putInt("reqInterval", requestInterval); 16 | preferences.end(); 17 | } 18 | 19 | void BmeHelperClass::setup() { 20 | unsigned long setupStartTime = millis(); 21 | Serial.println("Setup BME680 helper"); 22 | 23 | if (!bme.begin(0x76)) { 24 | Serial.println("Could not find a valid BME680 sensor, check wiring!"); 25 | return; 26 | } 27 | 28 | bme.setTemperatureOversampling(BME680_OS_8X); 29 | bme.setHumidityOversampling(BME680_OS_2X); 30 | bme.setPressureOversampling(BME680_OS_4X); 31 | bme.setIIRFilterSize(BME680_FILTER_SIZE_3); 32 | bme.setGasHeater(320, 150); // 320*C for 150 ms 33 | 34 | read(); 35 | 36 | setupDuration = millis() - setupStartTime; 37 | Serial.print("Setup CO2 helper took "); 38 | Serial.println(setupDuration); 39 | } 40 | 41 | void BmeHelperClass::loop() { 42 | if (millis() - readTimer >= 10000) { 43 | readValues(); 44 | readTimer = millis(); 45 | } 46 | if (millis() - requestTimer >= requestInterval) { 47 | sendValues(); 48 | requestTimer = millis(); 49 | } 50 | } 51 | 52 | void BmeHelperClass::sleep() { 53 | } 54 | 55 | void BmeHelperClass::readValues() { 56 | if (!bme.performReading()) { 57 | Serial.println("Failed to perform reading :("); 58 | return; 59 | } 60 | 61 | temp = bme.temperature; 62 | pressure = bme.pressure / 100.0; 63 | humidity = bme.humidity; 64 | gas = bme.gas_resistance / 1000.0; 65 | altitude = bme.readAltitude(SEALEVELPRESSURE_HPA); 66 | 67 | Serial.print("Temp (C): "); 68 | Serial.print(temp); 69 | 70 | Serial.print(" Humidity (%): "); 71 | Serial.print(humidity); 72 | 73 | Serial.print(" Pressure (hPa): "); 74 | Serial.print(pressure); 75 | 76 | Serial.print(" Altitude (m): "); 77 | Serial.print(altitude); 78 | 79 | Serial.print(" Gas (KOhms): "); 80 | Serial.println(gas); 81 | } 82 | 83 | void BmeHelperClass::sendValues() { 84 | if (host.length() == 0) return; 85 | if (!WiFiHelper.connect()) return; 86 | unsigned long requestStartTime = millis(); 87 | 88 | StaticJsonDocument<128> doc; 89 | 90 | udp.beginPacket(host.c_str(), port); 91 | 92 | doc["btmp"] = temp; 93 | doc["humidity"] = humidity; 94 | doc["pressure"] = pressure; 95 | doc["gas"] = gas; 96 | doc["altitude"] = altitude; 97 | 98 | serializeJson(doc, udp); 99 | 100 | udp.println(); 101 | udp.endPacket(); 102 | 103 | requestDuration = millis() - requestStartTime; 104 | Serial.print("Send UDP request "); 105 | Serial.println(requestDuration); 106 | } 107 | 108 | void BmeHelperClass::server() { 109 | Serial.println("Setup BME server"); 110 | 111 | WebServerHelper.server.on("/api/bme", HTTP_GET, [this](AsyncWebServerRequest *request) { 112 | int args = request->args(); 113 | 114 | if (args > 0) { 115 | request->send(200, "text/plain", "message received"); 116 | Serial.println("Update BME settings"); 117 | 118 | if (request->hasArg("host")) host = request->arg("host"); 119 | if (request->hasArg("port")) port = request->arg("port").toInt(); 120 | if (request->hasArg("requestInterval")) requestInterval = request->arg("requestInterval").toInt(); 121 | 122 | write(); 123 | 124 | } else { 125 | AsyncJsonResponse *response = new AsyncJsonResponse(); 126 | response->addHeader("Server", "ESP Async Web Server"); 127 | JsonVariant &root = response->getRoot(); 128 | 129 | root["setupDuration"] = setupDuration; 130 | root["requestDuration"] = requestDuration; 131 | 132 | root["host"] = host; 133 | root["port"] = port; 134 | 135 | root["requestInterval"] = requestInterval; 136 | 137 | root["temp"] = temp; 138 | root["humidity"] = humidity; 139 | root["pressure"] = pressure; 140 | root["gas"] = gas; 141 | root["altitude"] = altitude; 142 | 143 | response->setLength(); 144 | request->send(response); 145 | } 146 | }); 147 | } 148 | 149 | BmeHelperClass BmeHelper; 150 | -------------------------------------------------------------------------------- /lib/BmeHelper/BmeHelper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef BmeHelper_h 3 | #define BmeHelper_h 4 | 5 | #include "Adafruit_BME680.h" 6 | #include "Adafruit_Sensor.h" 7 | #include "Arduino.h" 8 | #include "ArduinoJson.h" 9 | #include "AsyncJson.h" 10 | #include "HTTPClient.h" 11 | #include "SPI.h" 12 | #include "WebServerHelper.h" 13 | #include "WiFiUdp.h" 14 | 15 | #define SEALEVELPRESSURE_HPA (1013.25) 16 | 17 | class BmeHelperClass { 18 | private: 19 | WiFiUDP udp; 20 | Adafruit_BME680 bme; 21 | Preferences preferences; 22 | 23 | int32_t requestInterval; 24 | unsigned long readTimer = 0; 25 | unsigned long requestTimer = 0; 26 | 27 | String host; 28 | int32_t port; 29 | 30 | void read(); 31 | void write(); 32 | 33 | void readValues(); 34 | void sendValues(); 35 | 36 | unsigned long requestDuration; 37 | unsigned long setupDuration; 38 | 39 | public: 40 | float temp, humidity, pressure, gas, altitude; 41 | 42 | void setup(); 43 | void server(); 44 | void loop(); 45 | void sleep(); 46 | }; 47 | 48 | extern BmeHelperClass BmeHelper; 49 | 50 | #endif -------------------------------------------------------------------------------- /lib/BmeHelper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BmeHelper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /lib/BsecHelper/BsecHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "BsecHelper.h" 2 | 3 | const uint8_t bsec_config_iaq[] = { 4 | #include "config/generic_33v_3s_4d/bsec_iaq.txt" 5 | }; 6 | 7 | void BsecHelperClass::read() { 8 | preferences.begin("bsec", true); 9 | host = preferences.getString("host"); 10 | port = preferences.getInt("port", 3333); 11 | requestInterval = preferences.getInt("reqInterval", 60000); 12 | hasState = preferences.getBool("hasState", false); 13 | tempOffset = preferences.getFloat("tempOffset", 13.5f); 14 | preferences.getBytes("state", bsecState, BSEC_MAX_STATE_BLOB_SIZE); 15 | preferences.end(); 16 | } 17 | 18 | void BsecHelperClass::write() { 19 | preferences.begin("bsec", false); 20 | preferences.putString("host", host); 21 | preferences.putInt("port", port); 22 | preferences.putInt("reqInterval", requestInterval); 23 | preferences.putFloat("tempOffset", tempOffset); 24 | preferences.end(); 25 | } 26 | 27 | void BsecHelperClass::writeState(bool set) { 28 | iaqSensor.getState(bsecState); 29 | Serial.println("Write State set: " + set ? "true" : "false"); 30 | for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) { 31 | Serial.print(bsecState[i], HEX); 32 | } 33 | Serial.println(""); 34 | 35 | preferences.begin("bsec", false); 36 | preferences.putBool("hasState", set); 37 | preferences.putBytes("state", bsecState, BSEC_MAX_STATE_BLOB_SIZE); 38 | preferences.end(); 39 | } 40 | 41 | void BsecHelperClass::checkSensor() { 42 | if (iaqSensor.status != BSEC_OK) { 43 | Serial.println("BSEC error code : " + String(iaqSensor.status)); 44 | } 45 | if (iaqSensor.bme680Status != BME680_OK) { 46 | Serial.println("BME680 error code : " + String(iaqSensor.bme680Status)); 47 | } 48 | } 49 | 50 | void BsecHelperClass::setup(uint8_t i2cAddr) { 51 | unsigned long setupStartTime = millis(); 52 | Serial.println("Setup BSEC helper"); 53 | 54 | Wire.begin(); 55 | iaqSensor.begin(i2cAddr, Wire); 56 | iaqSensor.setConfig(bsec_config_iaq); 57 | 58 | bsec_virtual_sensor_t sensorList[10] = { 59 | BSEC_OUTPUT_RAW_TEMPERATURE, 60 | BSEC_OUTPUT_RAW_PRESSURE, 61 | BSEC_OUTPUT_RAW_HUMIDITY, 62 | BSEC_OUTPUT_RAW_GAS, 63 | BSEC_OUTPUT_IAQ, 64 | BSEC_OUTPUT_STATIC_IAQ, 65 | BSEC_OUTPUT_CO2_EQUIVALENT, 66 | BSEC_OUTPUT_BREATH_VOC_EQUIVALENT, 67 | BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, 68 | BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, 69 | }; 70 | 71 | iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP); 72 | 73 | checkSensor(); 74 | read(); 75 | 76 | if (hasState) { 77 | Serial.println("Setup BSEC SetState"); 78 | iaqSensor.setState(bsecState); 79 | } 80 | 81 | iaqSensor.setTemperatureOffset(tempOffset); 82 | Serial.print("Setup BSEC helper temp offset "); 83 | Serial.println(tempOffset); 84 | 85 | udp.begin(port); 86 | 87 | setupDuration = millis() - setupStartTime; 88 | Serial.print("Setup BSEC helper took "); 89 | Serial.println(setupDuration); 90 | } 91 | 92 | void BsecHelperClass::loop() { 93 | readValues(); 94 | 95 | if (millis() - requestTimer >= requestInterval) { 96 | sendValues(); 97 | requestTimer = millis(); 98 | } 99 | } 100 | 101 | void BsecHelperClass::sleep() { 102 | } 103 | 104 | void BsecHelperClass::readValues() { 105 | if (iaqSensor.run()) { // If new data is available 106 | Serial.print(String(iaqSensor.rawTemperature)); 107 | Serial.print(", " + String(iaqSensor.pressure)); 108 | Serial.print(", " + String(iaqSensor.rawHumidity)); 109 | Serial.print(", " + String(iaqSensor.gasResistance)); 110 | Serial.print(", " + String(iaqSensor.iaq)); 111 | Serial.print(", " + String(iaqSensor.iaqAccuracy)); 112 | Serial.print(", " + String(iaqSensor.temperature)); 113 | Serial.print(", " + String(iaqSensor.humidity)); 114 | Serial.print(", " + String(iaqSensor.staticIaq)); 115 | Serial.print(", " + String(iaqSensor.co2Equivalent)); 116 | Serial.print(", " + String(iaqSensor.breathVocEquivalent)); 117 | 118 | Serial.println(""); 119 | updateState(); 120 | } else { 121 | checkSensor(); 122 | } 123 | } 124 | 125 | void BsecHelperClass::updateState() { 126 | if (stateUpdateCounter == 0) { 127 | if (iaqSensor.iaqAccuracy >= 3) { 128 | stateUpdateCounter++; 129 | writeState(true); 130 | } 131 | } else { 132 | if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) { 133 | stateUpdateCounter++; 134 | writeState(true); 135 | } 136 | } 137 | } 138 | 139 | void BsecHelperClass::sendValues() { 140 | if (host.length() == 0) return; 141 | if (!WiFiHelper.connect()) return; 142 | unsigned long requestStartTime = millis(); 143 | 144 | StaticJsonDocument<300> doc; 145 | 146 | udp.beginPacket(host.c_str(), port); 147 | 148 | doc["rtmp"] = iaqSensor.rawTemperature; 149 | doc["pressure"] = iaqSensor.pressure; 150 | doc["humidity"] = iaqSensor.rawHumidity; 151 | doc["gasResistance"] = iaqSensor.gasResistance; 152 | doc["iaq"] = iaqSensor.iaq; 153 | doc["iaqAccuracy"] = iaqSensor.iaqAccuracy; 154 | doc["temperature"] = iaqSensor.temperature; 155 | doc["humidity"] = iaqSensor.humidity; 156 | doc["staticIaq"] = iaqSensor.staticIaq; 157 | doc["co2Equivalent"] = iaqSensor.co2Equivalent; 158 | doc["breathVocEquivalent"] = iaqSensor.breathVocEquivalent; 159 | 160 | serializeJson(doc, udp); 161 | 162 | udp.println(); 163 | udp.endPacket(); 164 | 165 | requestDuration = millis() - requestStartTime; 166 | Serial.print("Send UDP request "); 167 | Serial.println(requestDuration); 168 | } 169 | 170 | void BsecHelperClass::server() { 171 | Serial.println("Setup BSEC server"); 172 | 173 | WebServerHelper.server.on("/api/bsec", HTTP_GET, [this](AsyncWebServerRequest *request) { 174 | int args = request->args(); 175 | 176 | if (args > 0) { 177 | request->send(200, "text/plain", "message received"); 178 | Serial.println("Update BSEC settings"); 179 | 180 | if (request->hasArg("host")) host = request->arg("host"); 181 | if (request->hasArg("port")) port = request->arg("port").toInt(); 182 | if (request->hasArg("requestInterval")) requestInterval = request->arg("requestInterval").toInt(); 183 | if (request->hasArg("tempOffset")) tempOffset = request->arg("tempOffset").toFloat(); 184 | 185 | write(); 186 | ESP.restart(); 187 | 188 | } else { 189 | AsyncJsonResponse *response = new AsyncJsonResponse(); 190 | response->addHeader("Server", "ESP Async Web Server"); 191 | JsonVariant &root = response->getRoot(); 192 | 193 | root["setupDuration"] = setupDuration; 194 | root["requestDuration"] = requestDuration; 195 | 196 | root["host"] = host; 197 | root["port"] = port; 198 | 199 | root["requestInterval"] = requestInterval; 200 | root["tempOffset"] = tempOffset; 201 | 202 | root["rtmp"] = iaqSensor.rawTemperature; 203 | root["pressure"] = iaqSensor.pressure; 204 | root["rawHumidity"] = iaqSensor.rawHumidity; 205 | root["gasResistance"] = iaqSensor.gasResistance; 206 | root["iaq"] = iaqSensor.iaq; 207 | root["iaqAccuracy"] = iaqSensor.iaqAccuracy; 208 | root["temperature"] = iaqSensor.temperature; 209 | root["humidity"] = iaqSensor.humidity; 210 | root["staticIaq"] = iaqSensor.staticIaq; 211 | root["co2Equivalent"] = iaqSensor.co2Equivalent; 212 | root["breathVocEquivalent"] = iaqSensor.breathVocEquivalent; 213 | 214 | response->setLength(); 215 | request->send(response); 216 | } 217 | }); 218 | 219 | WebServerHelper.server.on("/api/bsecState", HTTP_GET, [this](AsyncWebServerRequest *request) { 220 | writeState(!request->hasArg("reset")); 221 | request->send(200, "text/plain", "message received"); 222 | ESP.restart(); 223 | }); 224 | } 225 | 226 | BsecHelperClass BsecHelper; 227 | -------------------------------------------------------------------------------- /lib/BsecHelper/BsecHelper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef BsecHelper_h 3 | #define BsecHelper_h 4 | 5 | #include "Arduino.h" 6 | #include "ArduinoJson.h" 7 | #include "AsyncJson.h" 8 | #include "HTTPClient.h" 9 | #include "Preferences.h" 10 | #include "WebServerHelper.h" 11 | #include "WiFiUdp.h" 12 | #include "WifiHelper.h" 13 | #include "bsec.h" 14 | 15 | #define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // 360 minutes - 4 times a day 16 | 17 | class BsecHelperClass { 18 | private: 19 | WiFiUDP udp; 20 | Preferences preferences; 21 | 22 | int32_t requestInterval; 23 | unsigned long readTimer = 0; 24 | unsigned long requestTimer = 0; 25 | 26 | uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0}; 27 | uint16_t stateUpdateCounter = 0; 28 | 29 | String host; 30 | int32_t port; 31 | bool hasState; 32 | float tempOffset; 33 | 34 | void read(); 35 | void write(); 36 | 37 | void checkSensor(); 38 | void readValues(); 39 | void sendValues(); 40 | 41 | void updateState(); 42 | void writeState(bool write); 43 | 44 | unsigned long requestDuration; 45 | unsigned long setupDuration; 46 | 47 | public: 48 | Bsec iaqSensor; 49 | 50 | void setup(uint8_t i2cAddr); 51 | void server(); 52 | void loop(); 53 | void sleep(); 54 | }; 55 | 56 | extern BsecHelperClass BsecHelper; 57 | 58 | #endif -------------------------------------------------------------------------------- /lib/BsecHelper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BsecHelper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /lib/Co2Helper/Co2Helper.cpp: -------------------------------------------------------------------------------- 1 | #include "Co2Helper.h" 2 | 3 | Co2HelperClass::Co2HelperClass() : hwSerial(2){}; 4 | 5 | void Co2HelperClass::read() { 6 | preferences.begin("co2", true); 7 | host = preferences.getString("host"); 8 | port = preferences.getInt("port", 3333); 9 | autoCalibration = preferences.getBool("autoCalib", false); 10 | requestInterval = preferences.getInt("reqInterval", 60000); 11 | preferences.end(); 12 | } 13 | 14 | void Co2HelperClass::write() { 15 | preferences.begin("co2", false); 16 | preferences.putString("host", host); 17 | preferences.putInt("port", port); 18 | preferences.putBool("autoCalib", autoCalibration); 19 | preferences.putInt("reqInterval", requestInterval); 20 | preferences.end(); 21 | } 22 | 23 | void Co2HelperClass::setup() { 24 | unsigned long setupStartTime = millis(); 25 | Serial.println("Setup CO2 helper"); 26 | 27 | read(); 28 | 29 | hwSerial.begin(9600); 30 | 31 | mhz19.begin(hwSerial); 32 | mhz19.autoCalibration(autoCalibration); 33 | 34 | udp.begin(port); 35 | 36 | setupDuration = millis() - setupStartTime; 37 | Serial.print("Setup CO2 helper took "); 38 | Serial.println(setupDuration); 39 | } 40 | 41 | void Co2HelperClass::loop() { 42 | if (millis() - readTimer >= 10000) { 43 | readValues(); 44 | readTimer = millis(); 45 | } 46 | if (millis() - requestTimer >= requestInterval) { 47 | sendValues(); 48 | requestTimer = millis(); 49 | } 50 | } 51 | 52 | void Co2HelperClass::sleep() { 53 | } 54 | 55 | void Co2HelperClass::readValues() { 56 | int readCo2 = mhz19.getCO2(); 57 | float readTemp = mhz19.getTemperature(true); 58 | 59 | if (mhz19.errorCode == RESULT_OK && readCo2 != 0) { 60 | co2 = readCo2; 61 | temp = readTemp; 62 | 63 | Serial.print("CO2 (ppm): "); 64 | Serial.print(co2); 65 | 66 | Serial.print(" Temperature (C): "); 67 | Serial.println(temp); 68 | } else { 69 | Serial.print("Failed to read co2 sensor "); 70 | Serial.println(String(mhz19.errorCode)); 71 | } 72 | } 73 | 74 | void Co2HelperClass::sendValues() { 75 | if (host.length() == 0) return; 76 | if (!WiFiHelper.connect()) return; 77 | unsigned long requestStartTime = millis(); 78 | 79 | StaticJsonDocument<45> doc; 80 | 81 | udp.beginPacket(host.c_str(), port); 82 | 83 | doc["co2"] = co2; 84 | doc["ctmp"] = temp; 85 | serializeJson(doc, udp); 86 | 87 | udp.println(); 88 | udp.endPacket(); 89 | 90 | requestDuration = millis() - requestStartTime; 91 | Serial.print("Send UDP request "); 92 | Serial.println(requestDuration); 93 | } 94 | 95 | void Co2HelperClass::server() { 96 | Serial.println("Setup CO2 server"); 97 | 98 | WebServerHelper.server.on("/api/co2", HTTP_GET, [this](AsyncWebServerRequest *request) { 99 | int args = request->args(); 100 | 101 | if (args > 0) { 102 | request->send(200, "text/plain", "message received"); 103 | Serial.println("Update CO2 settings"); 104 | 105 | if (request->hasArg("host")) host = request->arg("host"); 106 | if (request->hasArg("port")) port = request->arg("port").toInt(); 107 | if (request->hasArg("autoCalibration")) autoCalibration = request->arg("autoCalibration") == "1"; 108 | if (request->hasArg("requestInterval")) requestInterval = request->arg("requestInterval").toInt(); 109 | 110 | write(); 111 | 112 | } else { 113 | AsyncJsonResponse *response = new AsyncJsonResponse(); 114 | response->addHeader("Server", "ESP Async Web Server"); 115 | JsonVariant &root = response->getRoot(); 116 | 117 | root["setupDuration"] = setupDuration; 118 | root["requestDuration"] = requestDuration; 119 | 120 | root["host"] = host; 121 | root["port"] = port; 122 | 123 | root["autoCalibration"] = autoCalibration ? 1 : 0; 124 | root["requestInterval"] = requestInterval; 125 | 126 | root["co2"] = co2; 127 | root["temp"] = temp; 128 | 129 | response->setLength(); 130 | request->send(response); 131 | } 132 | }); 133 | 134 | WebServerHelper.server.on("/api/co2Calibrate", HTTP_GET, [this](AsyncWebServerRequest *request) { 135 | mhz19.autoCalibration(false); 136 | mhz19.calibrate(); 137 | 138 | request->send(200, "text/plain", "message received"); 139 | }); 140 | } 141 | 142 | Co2HelperClass Co2Helper; 143 | -------------------------------------------------------------------------------- /lib/Co2Helper/Co2Helper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef Co2Helper_h 3 | #define Co2Helper_h 4 | 5 | #include "Arduino.h" 6 | #include "ArduinoJson.h" 7 | #include "AsyncJson.h" 8 | #include "HTTPClient.h" 9 | #include "MHZ19.h" 10 | #include "Preferences.h" 11 | #include "WebServerHelper.h" 12 | #include "WiFiHelper.h" 13 | #include "WiFiUdp.h" 14 | 15 | class Co2HelperClass { 16 | private: 17 | MHZ19 mhz19; 18 | WiFiUDP udp; 19 | Preferences preferences; 20 | 21 | int32_t requestInterval; 22 | unsigned long readTimer = 0; 23 | unsigned long requestTimer = 0; 24 | 25 | String host; 26 | int32_t port; 27 | boolean autoCalibration; 28 | 29 | void read(); 30 | void write(); 31 | 32 | void readValues(); 33 | void sendValues(); 34 | 35 | unsigned long requestDuration; 36 | unsigned long setupDuration; 37 | 38 | public: 39 | Co2HelperClass(); 40 | HardwareSerial hwSerial; 41 | 42 | float temp; 43 | int co2; 44 | 45 | void setup(); 46 | void server(); 47 | void loop(); 48 | void sleep(); 49 | }; 50 | 51 | extern Co2HelperClass Co2Helper; 52 | 53 | #endif -------------------------------------------------------------------------------- /lib/Co2Helper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Co2Helper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /lib/I2Cdev/I2Cdev.cpp: -------------------------------------------------------------------------------- 1 | // I2Cdev library collection - Main I2C device class 2 | // Abstracts bit and byte I2C R/W functions into a convenient class 3 | // 2013-06-05 by Jeff Rowberg 4 | // 5 | // Changelog: 6 | // 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications 7 | // 2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan) 8 | // 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire 9 | // - add compiler warnings when using outdated or IDE or limited I2Cdev implementation 10 | // 2011-11-01 - fix write*Bits mask calculation (thanks sasquatch @ Arduino forums) 11 | // 2011-10-03 - added automatic Arduino version detection for ease of use 12 | // 2011-10-02 - added Gene Knight's NBWire TwoWire class implementation with small modifications 13 | // 2011-08-31 - added support for Arduino 1.0 Wire library (methods are different from 0.x) 14 | // 2011-08-03 - added optional timeout parameter to read* methods to easily change from default 15 | // 2011-08-02 - added support for 16-bit registers 16 | // - fixed incorrect Doxygen comments on some methods 17 | // - added timeout value for read operations (thanks mem @ Arduino forums) 18 | // 2011-07-30 - changed read/write function structures to return success or byte counts 19 | // - made all methods static for multi-device memory savings 20 | // 2011-07-28 - initial release 21 | 22 | /* ============================================ 23 | I2Cdev device library code is placed under the MIT license 24 | Copyright (c) 2013 Jeff Rowberg 25 | 26 | Permission is hereby granted, free of charge, to any person obtaining a copy 27 | of this software and associated documentation files (the "Software"), to deal 28 | in the Software without restriction, including without limitation the rights 29 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 30 | copies of the Software, and to permit persons to whom the Software is 31 | furnished to do so, subject to the following conditions: 32 | 33 | The above copyright notice and this permission notice shall be included in 34 | all copies or substantial portions of the Software. 35 | 36 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 37 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 38 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 39 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 40 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 41 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 42 | THE SOFTWARE. 43 | =============================================== 44 | */ 45 | 46 | #include "I2Cdev.h" 47 | 48 | #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE 49 | 50 | #ifdef I2CDEV_IMPLEMENTATION_WARNINGS 51 | #if ARDUINO < 100 52 | #warning Using outdated Arduino IDE with Wire library is functionally limiting. 53 | #warning Arduino IDE v1.6.5+ with I2Cdev Fastwire implementation is recommended. 54 | #warning This I2Cdev implementation does not support: 55 | #warning - Repeated starts conditions 56 | #warning - Timeout detection (some Wire requests block forever) 57 | #elif ARDUINO == 100 58 | #warning Using outdated Arduino IDE with Wire library is functionally limiting. 59 | #warning Arduino IDE v1.6.5+ with I2Cdev Fastwire implementation is recommended. 60 | #warning This I2Cdev implementation does not support: 61 | #warning - Repeated starts conditions 62 | #warning - Timeout detection (some Wire requests block forever) 63 | #elif ARDUINO > 100 64 | /*#warning Using current Arduino IDE with Wire library is functionally limiting. 65 | #warning Arduino IDE v1.6.5+ with I2CDEV_BUILTIN_FASTWIRE implementation is recommended. 66 | #warning This I2Cdev implementation does not support: 67 | #warning - Timeout detection (some Wire requests block forever)*/ 68 | #endif 69 | #endif 70 | 71 | #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE 72 | 73 | //#error The I2CDEV_BUILTIN_FASTWIRE implementation is known to be broken right now. Patience, Iago! 74 | 75 | #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE 76 | 77 | #ifdef I2CDEV_IMPLEMENTATION_WARNINGS 78 | #warning Using I2CDEV_BUILTIN_NBWIRE implementation may adversely affect interrupt detection. 79 | #warning This I2Cdev implementation does not support: 80 | #warning - Repeated starts conditions 81 | #endif 82 | 83 | // NBWire implementation based heavily on code by Gene Knight 84 | // Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html 85 | // Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html 86 | TwoWire Wire; 87 | 88 | #endif 89 | 90 | #ifndef BUFFER_LENGTH 91 | // band-aid fix for platforms without Wire-defined BUFFER_LENGTH (removed from some official implementations) 92 | #define BUFFER_LENGTH 32 93 | #endif 94 | 95 | /** Default constructor. 96 | */ 97 | I2Cdev::I2Cdev() { 98 | } 99 | 100 | /** Read a single bit from an 8-bit device register. 101 | * @param devAddr I2C slave device address 102 | * @param regAddr Register regAddr to read from 103 | * @param bitNum Bit position to read (0-7) 104 | * @param data Container for single bit value 105 | * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) 106 | * @return Status of read operation (true = success) 107 | */ 108 | int8_t I2Cdev::readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout) { 109 | uint8_t b; 110 | uint8_t count = readByte(devAddr, regAddr, &b, timeout); 111 | *data = b & (1 << bitNum); 112 | return count; 113 | } 114 | 115 | /** Read a single bit from a 16-bit device register. 116 | * @param devAddr I2C slave device address 117 | * @param regAddr Register regAddr to read from 118 | * @param bitNum Bit position to read (0-15) 119 | * @param data Container for single bit value 120 | * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) 121 | * @return Status of read operation (true = success) 122 | */ 123 | int8_t I2Cdev::readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout) { 124 | uint16_t b; 125 | uint8_t count = readWord(devAddr, regAddr, &b, timeout); 126 | *data = b & (1 << bitNum); 127 | return count; 128 | } 129 | 130 | /** Read multiple bits from an 8-bit device register. 131 | * @param devAddr I2C slave device address 132 | * @param regAddr Register regAddr to read from 133 | * @param bitStart First bit position to read (0-7) 134 | * @param length Number of bits to read (not more than 8) 135 | * @param data Container for right-aligned value (i.e. '101' read from any bitStart position will equal 0x05) 136 | * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) 137 | * @return Status of read operation (true = success) 138 | */ 139 | int8_t I2Cdev::readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout) { 140 | // 01101001 read byte 141 | // 76543210 bit numbers 142 | // xxx args: bitStart=4, length=3 143 | // 010 masked 144 | // -> 010 shifted 145 | uint8_t count, b; 146 | if ((count = readByte(devAddr, regAddr, &b, timeout)) != 0) { 147 | uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); 148 | b &= mask; 149 | b >>= (bitStart - length + 1); 150 | *data = b; 151 | } 152 | return count; 153 | } 154 | 155 | /** Read multiple bits from a 16-bit device register. 156 | * @param devAddr I2C slave device address 157 | * @param regAddr Register regAddr to read from 158 | * @param bitStart First bit position to read (0-15) 159 | * @param length Number of bits to read (not more than 16) 160 | * @param data Container for right-aligned value (i.e. '101' read from any bitStart position will equal 0x05) 161 | * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) 162 | * @return Status of read operation (1 = success, 0 = failure, -1 = timeout) 163 | */ 164 | int8_t I2Cdev::readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout) { 165 | // 1101011001101001 read byte 166 | // fedcba9876543210 bit numbers 167 | // xxx args: bitStart=12, length=3 168 | // 010 masked 169 | // -> 010 shifted 170 | uint8_t count; 171 | uint16_t w; 172 | if ((count = readWord(devAddr, regAddr, &w, timeout)) != 0) { 173 | uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1); 174 | w &= mask; 175 | w >>= (bitStart - length + 1); 176 | *data = w; 177 | } 178 | return count; 179 | } 180 | 181 | /** Read single byte from an 8-bit device register. 182 | * @param devAddr I2C slave device address 183 | * @param regAddr Register regAddr to read from 184 | * @param data Container for byte value read from device 185 | * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) 186 | * @return Status of read operation (true = success) 187 | */ 188 | int8_t I2Cdev::readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout) { 189 | return readBytes(devAddr, regAddr, 1, data, timeout); 190 | } 191 | 192 | /** Read single word from a 16-bit device register. 193 | * @param devAddr I2C slave device address 194 | * @param regAddr Register regAddr to read from 195 | * @param data Container for word value read from device 196 | * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) 197 | * @return Status of read operation (true = success) 198 | */ 199 | int8_t I2Cdev::readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout) { 200 | return readWords(devAddr, regAddr, 1, data, timeout); 201 | } 202 | 203 | /** Read multiple bytes from an 8-bit device register. 204 | * @param devAddr I2C slave device address 205 | * @param regAddr First register regAddr to read from 206 | * @param length Number of bytes to read 207 | * @param data Buffer to store read data in 208 | * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) 209 | * @return Number of bytes read (-1 indicates failure) 210 | */ 211 | int8_t I2Cdev::readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout) { 212 | #ifdef I2CDEV_SERIAL_DEBUG 213 | Serial.print("I2C (0x"); 214 | Serial.print(devAddr, HEX); 215 | Serial.print(") reading "); 216 | Serial.print(length, DEC); 217 | Serial.print(" bytes from 0x"); 218 | Serial.print(regAddr, HEX); 219 | Serial.print("..."); 220 | #endif 221 | 222 | int8_t count = 0; 223 | uint32_t t1 = millis(); 224 | 225 | #if (I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) 226 | 227 | #if (ARDUINO < 100) 228 | // Arduino v00xx (before v1.0), Wire library 229 | 230 | // I2C/TWI subsystem uses internal buffer that breaks with large data requests 231 | // so if user requests more than BUFFER_LENGTH bytes, we have to do it in 232 | // smaller chunks instead of all at once 233 | for (uint8_t k = 0; k < length; k += min((int)length, BUFFER_LENGTH)) { 234 | Wire.beginTransmission(devAddr); 235 | Wire.send(regAddr); 236 | Wire.endTransmission(); 237 | Wire.beginTransmission(devAddr); 238 | Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); 239 | 240 | for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { 241 | data[count] = Wire.receive(); 242 | #ifdef I2CDEV_SERIAL_DEBUG 243 | Serial.print(data[count], HEX); 244 | if (count + 1 < length) Serial.print(" "); 245 | #endif 246 | } 247 | 248 | Wire.endTransmission(); 249 | } 250 | #elif (ARDUINO == 100) 251 | // Arduino v1.0.0, Wire library 252 | // Adds standardized write() and read() stream methods instead of send() and receive() 253 | 254 | // I2C/TWI subsystem uses internal buffer that breaks with large data requests 255 | // so if user requests more than BUFFER_LENGTH bytes, we have to do it in 256 | // smaller chunks instead of all at once 257 | for (uint8_t k = 0; k < length; k += min((int)length, BUFFER_LENGTH)) { 258 | Wire.beginTransmission(devAddr); 259 | Wire.write(regAddr); 260 | Wire.endTransmission(); 261 | Wire.beginTransmission(devAddr); 262 | Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); 263 | 264 | for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { 265 | data[count] = Wire.read(); 266 | #ifdef I2CDEV_SERIAL_DEBUG 267 | Serial.print(data[count], HEX); 268 | if (count + 1 < length) Serial.print(" "); 269 | #endif 270 | } 271 | 272 | Wire.endTransmission(); 273 | } 274 | #elif (ARDUINO > 100) 275 | // Arduino v1.0.1+, Wire library 276 | // Adds official support for repeated start condition, yay! 277 | 278 | // I2C/TWI subsystem uses internal buffer that breaks with large data requests 279 | // so if user requests more than BUFFER_LENGTH bytes, we have to do it in 280 | // smaller chunks instead of all at once 281 | for (uint8_t k = 0; k < length; k += min((int)length, BUFFER_LENGTH)) { 282 | Wire.beginTransmission(devAddr); 283 | Wire.write(regAddr); 284 | Wire.endTransmission(); 285 | Wire.beginTransmission(devAddr); 286 | Wire.requestFrom(devAddr, (uint8_t)min(length - k, BUFFER_LENGTH)); 287 | 288 | for (; Wire.available() && (timeout == 0 || millis() - t1 < timeout); count++) { 289 | data[count] = Wire.read(); 290 | #ifdef I2CDEV_SERIAL_DEBUG 291 | Serial.print(data[count], HEX); 292 | if (count + 1 < length) Serial.print(" "); 293 | #endif 294 | } 295 | } 296 | #endif 297 | 298 | #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) 299 | 300 | // Fastwire library 301 | // no loop required for fastwire 302 | uint8_t status = Fastwire::readBuf(devAddr << 1, regAddr, data, length); 303 | if (status == 0) { 304 | count = length; // success 305 | } else { 306 | count = -1; // error 307 | } 308 | 309 | #endif 310 | 311 | // check for timeout 312 | if (timeout > 0 && millis() - t1 >= timeout && count < length) count = -1; // timeout 313 | 314 | #ifdef I2CDEV_SERIAL_DEBUG 315 | Serial.print(". Done ("); 316 | Serial.print(count, DEC); 317 | Serial.println(" read)."); 318 | #endif 319 | 320 | return count; 321 | } 322 | 323 | /** Read multiple words from a 16-bit device register. 324 | * @param devAddr I2C slave device address 325 | * @param regAddr First register regAddr to read from 326 | * @param length Number of words to read 327 | * @param data Buffer to store read data in 328 | * @param timeout Optional read timeout in milliseconds (0 to disable, leave off to use default class value in I2Cdev::readTimeout) 329 | * @return Number of words read (-1 indicates failure) 330 | */ 331 | int8_t I2Cdev::readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout) { 332 | #ifdef I2CDEV_SERIAL_DEBUG 333 | Serial.print("I2C (0x"); 334 | Serial.print(devAddr, HEX); 335 | Serial.print(") reading "); 336 | Serial.print(length, DEC); 337 | Serial.print(" words from 0x"); 338 | Serial.print(regAddr, HEX); 339 | Serial.print("..."); 340 | #endif 341 | 342 | int8_t count = 0; 343 | uint32_t t1 = millis(); 344 | 345 | #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE 346 | 347 | #if (ARDUINO < 100) 348 | // Arduino v00xx (before v1.0), Wire library 349 | 350 | // I2C/TWI subsystem uses internal buffer that breaks with large data requests 351 | // so if user requests more than BUFFER_LENGTH bytes, we have to do it in 352 | // smaller chunks instead of all at once 353 | for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) { 354 | Wire.beginTransmission(devAddr); 355 | Wire.send(regAddr); 356 | Wire.endTransmission(); 357 | Wire.beginTransmission(devAddr); 358 | Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes 359 | 360 | bool msb = true; // starts with MSB, then LSB 361 | for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { 362 | if (msb) { 363 | // first byte is bits 15-8 (MSb=15) 364 | data[count] = Wire.receive() << 8; 365 | } else { 366 | // second byte is bits 7-0 (LSb=0) 367 | data[count] |= Wire.receive(); 368 | #ifdef I2CDEV_SERIAL_DEBUG 369 | Serial.print(data[count], HEX); 370 | if (count + 1 < length) Serial.print(" "); 371 | #endif 372 | count++; 373 | } 374 | msb = !msb; 375 | } 376 | 377 | Wire.endTransmission(); 378 | } 379 | #elif (ARDUINO == 100) 380 | // Arduino v1.0.0, Wire library 381 | // Adds standardized write() and read() stream methods instead of send() and receive() 382 | 383 | // I2C/TWI subsystem uses internal buffer that breaks with large data requests 384 | // so if user requests more than BUFFER_LENGTH bytes, we have to do it in 385 | // smaller chunks instead of all at once 386 | for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) { 387 | Wire.beginTransmission(devAddr); 388 | Wire.write(regAddr); 389 | Wire.endTransmission(); 390 | Wire.beginTransmission(devAddr); 391 | Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes 392 | 393 | bool msb = true; // starts with MSB, then LSB 394 | for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { 395 | if (msb) { 396 | // first byte is bits 15-8 (MSb=15) 397 | data[count] = Wire.read() << 8; 398 | } else { 399 | // second byte is bits 7-0 (LSb=0) 400 | data[count] |= Wire.read(); 401 | #ifdef I2CDEV_SERIAL_DEBUG 402 | Serial.print(data[count], HEX); 403 | if (count + 1 < length) Serial.print(" "); 404 | #endif 405 | count++; 406 | } 407 | msb = !msb; 408 | } 409 | 410 | Wire.endTransmission(); 411 | } 412 | #elif (ARDUINO > 100) 413 | // Arduino v1.0.1+, Wire library 414 | // Adds official support for repeated start condition, yay! 415 | 416 | // I2C/TWI subsystem uses internal buffer that breaks with large data requests 417 | // so if user requests more than BUFFER_LENGTH bytes, we have to do it in 418 | // smaller chunks instead of all at once 419 | for (uint8_t k = 0; k < length * 2; k += min(length * 2, BUFFER_LENGTH)) { 420 | Wire.beginTransmission(devAddr); 421 | Wire.write(regAddr); 422 | Wire.endTransmission(); 423 | Wire.beginTransmission(devAddr); 424 | Wire.requestFrom(devAddr, (uint8_t)(length * 2)); // length=words, this wants bytes 425 | 426 | bool msb = true; // starts with MSB, then LSB 427 | for (; Wire.available() && count < length && (timeout == 0 || millis() - t1 < timeout);) { 428 | if (msb) { 429 | // first byte is bits 15-8 (MSb=15) 430 | data[count] = Wire.read() << 8; 431 | } else { 432 | // second byte is bits 7-0 (LSb=0) 433 | data[count] |= Wire.read(); 434 | #ifdef I2CDEV_SERIAL_DEBUG 435 | Serial.print(data[count], HEX); 436 | if (count + 1 < length) Serial.print(" "); 437 | #endif 438 | count++; 439 | } 440 | msb = !msb; 441 | } 442 | 443 | Wire.endTransmission(); 444 | } 445 | #endif 446 | 447 | #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) 448 | 449 | // Fastwire library 450 | // no loop required for fastwire 451 | uint8_t intermediate[(uint8_t)length*2]; 452 | uint8_t status = Fastwire::readBuf(devAddr << 1, regAddr, intermediate, (uint8_t)(length * 2)); 453 | if (status == 0) { 454 | count = length; // success 455 | for (uint8_t i = 0; i < length; i++) { 456 | data[i] = (intermediate[2*i] << 8) | intermediate[2*i + 1]; 457 | } 458 | } else { 459 | count = -1; // error 460 | } 461 | 462 | #endif 463 | 464 | if (timeout > 0 && millis() - t1 >= timeout && count < length) count = -1; // timeout 465 | 466 | #ifdef I2CDEV_SERIAL_DEBUG 467 | Serial.print(". Done ("); 468 | Serial.print(count, DEC); 469 | Serial.println(" read)."); 470 | #endif 471 | 472 | return count; 473 | } 474 | 475 | /** write a single bit in an 8-bit device register. 476 | * @param devAddr I2C slave device address 477 | * @param regAddr Register regAddr to write to 478 | * @param bitNum Bit position to write (0-7) 479 | * @param value New bit value to write 480 | * @return Status of operation (true = success) 481 | */ 482 | bool I2Cdev::writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data) { 483 | uint8_t b; 484 | readByte(devAddr, regAddr, &b); 485 | b = (data != 0) ? (b | (1 << bitNum)) : (b & ~(1 << bitNum)); 486 | return writeByte(devAddr, regAddr, b); 487 | } 488 | 489 | /** write a single bit in a 16-bit device register. 490 | * @param devAddr I2C slave device address 491 | * @param regAddr Register regAddr to write to 492 | * @param bitNum Bit position to write (0-15) 493 | * @param value New bit value to write 494 | * @return Status of operation (true = success) 495 | */ 496 | bool I2Cdev::writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data) { 497 | uint16_t w; 498 | readWord(devAddr, regAddr, &w); 499 | w = (data != 0) ? (w | (1 << bitNum)) : (w & ~(1 << bitNum)); 500 | return writeWord(devAddr, regAddr, w); 501 | } 502 | 503 | /** Write multiple bits in an 8-bit device register. 504 | * @param devAddr I2C slave device address 505 | * @param regAddr Register regAddr to write to 506 | * @param bitStart First bit position to write (0-7) 507 | * @param length Number of bits to write (not more than 8) 508 | * @param data Right-aligned value to write 509 | * @return Status of operation (true = success) 510 | */ 511 | bool I2Cdev::writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data) { 512 | // 010 value to write 513 | // 76543210 bit numbers 514 | // xxx args: bitStart=4, length=3 515 | // 00011100 mask byte 516 | // 10101111 original value (sample) 517 | // 10100011 original & ~mask 518 | // 10101011 masked | value 519 | uint8_t b; 520 | if (readByte(devAddr, regAddr, &b) != 0) { 521 | uint8_t mask = ((1 << length) - 1) << (bitStart - length + 1); 522 | data <<= (bitStart - length + 1); // shift data into correct position 523 | data &= mask; // zero all non-important bits in data 524 | b &= ~(mask); // zero all important bits in existing byte 525 | b |= data; // combine data with existing byte 526 | return writeByte(devAddr, regAddr, b); 527 | } else { 528 | return false; 529 | } 530 | } 531 | 532 | /** Write multiple bits in a 16-bit device register. 533 | * @param devAddr I2C slave device address 534 | * @param regAddr Register regAddr to write to 535 | * @param bitStart First bit position to write (0-15) 536 | * @param length Number of bits to write (not more than 16) 537 | * @param data Right-aligned value to write 538 | * @return Status of operation (true = success) 539 | */ 540 | bool I2Cdev::writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data) { 541 | // 010 value to write 542 | // fedcba9876543210 bit numbers 543 | // xxx args: bitStart=12, length=3 544 | // 0001110000000000 mask word 545 | // 1010111110010110 original value (sample) 546 | // 1010001110010110 original & ~mask 547 | // 1010101110010110 masked | value 548 | uint16_t w; 549 | if (readWord(devAddr, regAddr, &w) != 0) { 550 | uint16_t mask = ((1 << length) - 1) << (bitStart - length + 1); 551 | data <<= (bitStart - length + 1); // shift data into correct position 552 | data &= mask; // zero all non-important bits in data 553 | w &= ~(mask); // zero all important bits in existing word 554 | w |= data; // combine data with existing word 555 | return writeWord(devAddr, regAddr, w); 556 | } else { 557 | return false; 558 | } 559 | } 560 | 561 | /** Write single byte to an 8-bit device register. 562 | * @param devAddr I2C slave device address 563 | * @param regAddr Register address to write to 564 | * @param data New byte value to write 565 | * @return Status of operation (true = success) 566 | */ 567 | bool I2Cdev::writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) { 568 | return writeBytes(devAddr, regAddr, 1, &data); 569 | } 570 | 571 | /** Write single word to a 16-bit device register. 572 | * @param devAddr I2C slave device address 573 | * @param regAddr Register address to write to 574 | * @param data New word value to write 575 | * @return Status of operation (true = success) 576 | */ 577 | bool I2Cdev::writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data) { 578 | return writeWords(devAddr, regAddr, 1, &data); 579 | } 580 | 581 | /** Write multiple bytes to an 8-bit device register. 582 | * @param devAddr I2C slave device address 583 | * @param regAddr First register address to write to 584 | * @param length Number of bytes to write 585 | * @param data Buffer to copy new data from 586 | * @return Status of operation (true = success) 587 | */ 588 | bool I2Cdev::writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t* data) { 589 | #ifdef I2CDEV_SERIAL_DEBUG 590 | Serial.print("I2C (0x"); 591 | Serial.print(devAddr, HEX); 592 | Serial.print(") writing "); 593 | Serial.print(length, DEC); 594 | Serial.print(" bytes to 0x"); 595 | Serial.print(regAddr, HEX); 596 | Serial.print("..."); 597 | #endif 598 | uint8_t status = 0; 599 | #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) 600 | Wire.beginTransmission(devAddr); 601 | Wire.send((uint8_t) regAddr); // send address 602 | #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ 603 | || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ 604 | || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) 605 | Wire.beginTransmission(devAddr); 606 | Wire.write((uint8_t) regAddr); // send address 607 | #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) 608 | Fastwire::beginTransmission(devAddr); 609 | Fastwire::write(regAddr); 610 | #endif 611 | for (uint8_t i = 0; i < length; i++) { 612 | #ifdef I2CDEV_SERIAL_DEBUG 613 | Serial.print(data[i], HEX); 614 | if (i + 1 < length) Serial.print(" "); 615 | #endif 616 | #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) 617 | Wire.send((uint8_t) data[i]); 618 | #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ 619 | || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ 620 | || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) 621 | Wire.write((uint8_t) data[i]); 622 | #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) 623 | Fastwire::write((uint8_t) data[i]); 624 | #endif 625 | } 626 | #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) 627 | Wire.endTransmission(); 628 | #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ 629 | || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ 630 | || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) 631 | status = Wire.endTransmission(); 632 | #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) 633 | Fastwire::stop(); 634 | //status = Fastwire::endTransmission(); 635 | #endif 636 | #ifdef I2CDEV_SERIAL_DEBUG 637 | Serial.println(". Done."); 638 | #endif 639 | return status == 0; 640 | } 641 | 642 | /** Write multiple words to a 16-bit device register. 643 | * @param devAddr I2C slave device address 644 | * @param regAddr First register address to write to 645 | * @param length Number of words to write 646 | * @param data Buffer to copy new data from 647 | * @return Status of operation (true = success) 648 | */ 649 | bool I2Cdev::writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t* data) { 650 | #ifdef I2CDEV_SERIAL_DEBUG 651 | Serial.print("I2C (0x"); 652 | Serial.print(devAddr, HEX); 653 | Serial.print(") writing "); 654 | Serial.print(length, DEC); 655 | Serial.print(" words to 0x"); 656 | Serial.print(regAddr, HEX); 657 | Serial.print("..."); 658 | #endif 659 | uint8_t status = 0; 660 | #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) 661 | Wire.beginTransmission(devAddr); 662 | Wire.send(regAddr); // send address 663 | #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ 664 | || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ 665 | || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) 666 | Wire.beginTransmission(devAddr); 667 | Wire.write(regAddr); // send address 668 | #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) 669 | Fastwire::beginTransmission(devAddr); 670 | Fastwire::write(regAddr); 671 | #endif 672 | for (uint8_t i = 0; i < length; i++) { 673 | #ifdef I2CDEV_SERIAL_DEBUG 674 | Serial.print(data[i], HEX); 675 | if (i + 1 < length) Serial.print(" "); 676 | #endif 677 | #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) 678 | Wire.send((uint8_t)(data[i] >> 8)); // send MSB 679 | Wire.send((uint8_t)data[i]); // send LSB 680 | #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ 681 | || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ 682 | || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) 683 | Wire.write((uint8_t)(data[i] >> 8)); // send MSB 684 | Wire.write((uint8_t)data[i]); // send LSB 685 | #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) 686 | Fastwire::write((uint8_t)(data[i] >> 8)); // send MSB 687 | status = Fastwire::write((uint8_t)data[i]); // send LSB 688 | if (status != 0) break; 689 | #endif 690 | } 691 | #if ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO < 100) || I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE) 692 | Wire.endTransmission(); 693 | #elif ((I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE && ARDUINO >= 100) \ 694 | || (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE && ARDUINO >= 100) \ 695 | || I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE) 696 | status = Wire.endTransmission(); 697 | #elif (I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE) 698 | Fastwire::stop(); 699 | //status = Fastwire::endTransmission(); 700 | #endif 701 | #ifdef I2CDEV_SERIAL_DEBUG 702 | Serial.println(". Done."); 703 | #endif 704 | return status == 0; 705 | } 706 | 707 | /** Default timeout value for read operations. 708 | * Set this to 0 to disable timeout detection. 709 | */ 710 | uint16_t I2Cdev::readTimeout = I2CDEV_DEFAULT_READ_TIMEOUT; 711 | 712 | #if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE 713 | // I2C library 714 | ////////////////////// 715 | // Copyright(C) 2012 716 | // Francesco Ferrara 717 | // ferrara[at]libero[point]it 718 | ////////////////////// 719 | 720 | /* 721 | FastWire 722 | - 0.24 added stop 723 | - 0.23 added reset 724 | 725 | This is a library to help faster programs to read I2C devices. 726 | Copyright(C) 2012 Francesco Ferrara 727 | occhiobello at gmail dot com 728 | [used by Jeff Rowberg for I2Cdevlib with permission] 729 | */ 730 | 731 | boolean Fastwire::waitInt() { 732 | int l = 250; 733 | while (!(TWCR & (1 << TWINT)) && l-- > 0); 734 | return l > 0; 735 | } 736 | 737 | void Fastwire::setup(int khz, boolean pullup) { 738 | TWCR = 0; 739 | #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega328P__) 740 | // activate internal pull-ups for twi (PORTC bits 4 & 5) 741 | // as per note from atmega8 manual pg167 742 | if (pullup) PORTC |= ((1 << 4) | (1 << 5)); 743 | else PORTC &= ~((1 << 4) | (1 << 5)); 744 | #elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) 745 | // activate internal pull-ups for twi (PORTC bits 0 & 1) 746 | if (pullup) PORTC |= ((1 << 0) | (1 << 1)); 747 | else PORTC &= ~((1 << 0) | (1 << 1)); 748 | #else 749 | // activate internal pull-ups for twi (PORTD bits 0 & 1) 750 | // as per note from atmega128 manual pg204 751 | if (pullup) PORTD |= ((1 << 0) | (1 << 1)); 752 | else PORTD &= ~((1 << 0) | (1 << 1)); 753 | #endif 754 | 755 | TWSR = 0; // no prescaler => prescaler = 1 756 | TWBR = ((16000L / khz) - 16) / 2; // change the I2C clock rate 757 | TWCR = 1 << TWEN; // enable twi module, no interrupt 758 | } 759 | 760 | // added by Jeff Rowberg 2013-05-07: 761 | // Arduino Wire-style "beginTransmission" function 762 | // (takes 7-bit device address like the Wire method, NOT 8-bit: 0x68, not 0xD0/0xD1) 763 | byte Fastwire::beginTransmission(byte device) { 764 | byte twst, retry; 765 | retry = 2; 766 | do { 767 | TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); 768 | if (!waitInt()) return 1; 769 | twst = TWSR & 0xF8; 770 | if (twst != TW_START && twst != TW_REP_START) return 2; 771 | 772 | //Serial.print(device, HEX); 773 | //Serial.print(" "); 774 | TWDR = device << 1; // send device address without read bit (1) 775 | TWCR = (1 << TWINT) | (1 << TWEN); 776 | if (!waitInt()) return 3; 777 | twst = TWSR & 0xF8; 778 | } while (twst == TW_MT_SLA_NACK && retry-- > 0); 779 | if (twst != TW_MT_SLA_ACK) return 4; 780 | return 0; 781 | } 782 | 783 | byte Fastwire::writeBuf(byte device, byte address, byte *data, byte num) { 784 | byte twst, retry; 785 | 786 | retry = 2; 787 | do { 788 | TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); 789 | if (!waitInt()) return 1; 790 | twst = TWSR & 0xF8; 791 | if (twst != TW_START && twst != TW_REP_START) return 2; 792 | 793 | //Serial.print(device, HEX); 794 | //Serial.print(" "); 795 | TWDR = device & 0xFE; // send device address without read bit (1) 796 | TWCR = (1 << TWINT) | (1 << TWEN); 797 | if (!waitInt()) return 3; 798 | twst = TWSR & 0xF8; 799 | } while (twst == TW_MT_SLA_NACK && retry-- > 0); 800 | if (twst != TW_MT_SLA_ACK) return 4; 801 | 802 | //Serial.print(address, HEX); 803 | //Serial.print(" "); 804 | TWDR = address; // send data to the previously addressed device 805 | TWCR = (1 << TWINT) | (1 << TWEN); 806 | if (!waitInt()) return 5; 807 | twst = TWSR & 0xF8; 808 | if (twst != TW_MT_DATA_ACK) return 6; 809 | 810 | for (byte i = 0; i < num; i++) { 811 | //Serial.print(data[i], HEX); 812 | //Serial.print(" "); 813 | TWDR = data[i]; // send data to the previously addressed device 814 | TWCR = (1 << TWINT) | (1 << TWEN); 815 | if (!waitInt()) return 7; 816 | twst = TWSR & 0xF8; 817 | if (twst != TW_MT_DATA_ACK) return 8; 818 | } 819 | //Serial.print("\n"); 820 | 821 | return 0; 822 | } 823 | 824 | byte Fastwire::write(byte value) { 825 | byte twst; 826 | //Serial.println(value, HEX); 827 | TWDR = value; // send data 828 | TWCR = (1 << TWINT) | (1 << TWEN); 829 | if (!waitInt()) return 1; 830 | twst = TWSR & 0xF8; 831 | if (twst != TW_MT_DATA_ACK) return 2; 832 | return 0; 833 | } 834 | 835 | byte Fastwire::readBuf(byte device, byte address, byte *data, byte num) { 836 | byte twst, retry; 837 | 838 | retry = 2; 839 | do { 840 | TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); 841 | if (!waitInt()) return 16; 842 | twst = TWSR & 0xF8; 843 | if (twst != TW_START && twst != TW_REP_START) return 17; 844 | 845 | //Serial.print(device, HEX); 846 | //Serial.print(" "); 847 | TWDR = device & 0xfe; // send device address to write 848 | TWCR = (1 << TWINT) | (1 << TWEN); 849 | if (!waitInt()) return 18; 850 | twst = TWSR & 0xF8; 851 | } while (twst == TW_MT_SLA_NACK && retry-- > 0); 852 | if (twst != TW_MT_SLA_ACK) return 19; 853 | 854 | //Serial.print(address, HEX); 855 | //Serial.print(" "); 856 | TWDR = address; // send data to the previously addressed device 857 | TWCR = (1 << TWINT) | (1 << TWEN); 858 | if (!waitInt()) return 20; 859 | twst = TWSR & 0xF8; 860 | if (twst != TW_MT_DATA_ACK) return 21; 861 | 862 | /***/ 863 | 864 | retry = 2; 865 | do { 866 | TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO) | (1 << TWSTA); 867 | if (!waitInt()) return 22; 868 | twst = TWSR & 0xF8; 869 | if (twst != TW_START && twst != TW_REP_START) return 23; 870 | 871 | //Serial.print(device, HEX); 872 | //Serial.print(" "); 873 | TWDR = device | 0x01; // send device address with the read bit (1) 874 | TWCR = (1 << TWINT) | (1 << TWEN); 875 | if (!waitInt()) return 24; 876 | twst = TWSR & 0xF8; 877 | } while (twst == TW_MR_SLA_NACK && retry-- > 0); 878 | if (twst != TW_MR_SLA_ACK) return 25; 879 | 880 | for (uint8_t i = 0; i < num; i++) { 881 | if (i == num - 1) 882 | TWCR = (1 << TWINT) | (1 << TWEN); 883 | else 884 | TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWEA); 885 | if (!waitInt()) return 26; 886 | twst = TWSR & 0xF8; 887 | if (twst != TW_MR_DATA_ACK && twst != TW_MR_DATA_NACK) return twst; 888 | data[i] = TWDR; 889 | //Serial.print(data[i], HEX); 890 | //Serial.print(" "); 891 | } 892 | //Serial.print("\n"); 893 | stop(); 894 | 895 | return 0; 896 | } 897 | 898 | void Fastwire::reset() { 899 | TWCR = 0; 900 | } 901 | 902 | byte Fastwire::stop() { 903 | TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); 904 | if (!waitInt()) return 1; 905 | return 0; 906 | } 907 | #endif 908 | 909 | #if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE 910 | // NBWire implementation based heavily on code by Gene Knight 911 | // Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html 912 | // Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html 913 | 914 | /* 915 | call this version 1.0 916 | 917 | Offhand, the only funky part that I can think of is in nbrequestFrom, where the buffer 918 | length and index are set *before* the data is actually read. The problem is that these 919 | are variables local to the TwoWire object, and by the time we actually have read the 920 | data, and know what the length actually is, we have no simple access to the object's 921 | variables. The actual bytes read *is* given to the callback function, though. 922 | 923 | The ISR code for a slave receiver is commented out. I don't have that setup, and can't 924 | verify it at this time. Save it for 2.0! 925 | 926 | The handling of the read and write processes here is much like in the demo sketch code: 927 | the process is broken down into sequential functions, where each registers the next as a 928 | callback, essentially. 929 | 930 | For example, for the Read process, twi_read00 just returns if TWI is not yet in a 931 | ready state. When there's another interrupt, and the interface *is* ready, then it 932 | sets up the read, starts it, and registers twi_read01 as the function to call after 933 | the *next* interrupt. twi_read01, then, just returns if the interface is still in a 934 | "reading" state. When the reading is done, it copies the information to the buffer, 935 | cleans up, and calls the user-requested callback function with the actual number of 936 | bytes read. 937 | 938 | The writing is similar. 939 | 940 | Questions, comments and problems can go to Gene@Telobot.com. 941 | 942 | Thumbs Up! 943 | Gene Knight 944 | 945 | */ 946 | 947 | uint8_t TwoWire::rxBuffer[NBWIRE_BUFFER_LENGTH]; 948 | uint8_t TwoWire::rxBufferIndex = 0; 949 | uint8_t TwoWire::rxBufferLength = 0; 950 | 951 | uint8_t TwoWire::txAddress = 0; 952 | uint8_t TwoWire::txBuffer[NBWIRE_BUFFER_LENGTH]; 953 | uint8_t TwoWire::txBufferIndex = 0; 954 | uint8_t TwoWire::txBufferLength = 0; 955 | 956 | //uint8_t TwoWire::transmitting = 0; 957 | void (*TwoWire::user_onRequest)(void); 958 | void (*TwoWire::user_onReceive)(int); 959 | 960 | static volatile uint8_t twi_transmitting; 961 | static volatile uint8_t twi_state; 962 | static uint8_t twi_slarw; 963 | static volatile uint8_t twi_error; 964 | static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH]; 965 | static volatile uint8_t twi_masterBufferIndex; 966 | static uint8_t twi_masterBufferLength; 967 | static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH]; 968 | static volatile uint8_t twi_rxBufferIndex; 969 | //static volatile uint8_t twi_Interrupt_Continue_Command; 970 | static volatile uint8_t twi_Return_Value; 971 | static volatile uint8_t twi_Done; 972 | void (*twi_cbendTransmissionDone)(int); 973 | void (*twi_cbreadFromDone)(int); 974 | 975 | void twi_init() { 976 | // initialize state 977 | twi_state = TWI_READY; 978 | 979 | // activate internal pull-ups for twi 980 | // as per note from atmega8 manual pg167 981 | sbi(PORTC, 4); 982 | sbi(PORTC, 5); 983 | 984 | // initialize twi prescaler and bit rate 985 | cbi(TWSR, TWPS0); // TWI Status Register - Prescaler bits 986 | cbi(TWSR, TWPS1); 987 | 988 | /* twi bit rate formula from atmega128 manual pg 204 989 | SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR)) 990 | note: TWBR should be 10 or higher for master mode 991 | It is 72 for a 16mhz Wiring board with 100kHz TWI */ 992 | 993 | TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2; // bitrate register 994 | // enable twi module, acks, and twi interrupt 995 | 996 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA); 997 | 998 | /* TWEN - TWI Enable Bit 999 | TWIE - TWI Interrupt Enable 1000 | TWEA - TWI Enable Acknowledge Bit 1001 | TWINT - TWI Interrupt Flag 1002 | TWSTA - TWI Start Condition 1003 | */ 1004 | } 1005 | 1006 | typedef struct { 1007 | uint8_t address; 1008 | uint8_t* data; 1009 | uint8_t length; 1010 | uint8_t wait; 1011 | uint8_t i; 1012 | } twi_Write_Vars; 1013 | 1014 | twi_Write_Vars *ptwv = 0; 1015 | static void (*fNextInterruptFunction)(void) = 0; 1016 | 1017 | void twi_Finish(byte bRetVal) { 1018 | if (ptwv) { 1019 | free(ptwv); 1020 | ptwv = 0; 1021 | } 1022 | twi_Done = 0xFF; 1023 | twi_Return_Value = bRetVal; 1024 | fNextInterruptFunction = 0; 1025 | } 1026 | 1027 | uint8_t twii_WaitForDone(uint16_t timeout) { 1028 | uint32_t endMillis = millis() + timeout; 1029 | while (!twi_Done && (timeout == 0 || millis() < endMillis)) continue; 1030 | return twi_Return_Value; 1031 | } 1032 | 1033 | void twii_SetState(uint8_t ucState) { 1034 | twi_state = ucState; 1035 | } 1036 | 1037 | void twii_SetError(uint8_t ucError) { 1038 | twi_error = ucError ; 1039 | } 1040 | 1041 | void twii_InitBuffer(uint8_t ucPos, uint8_t ucLength) { 1042 | twi_masterBufferIndex = 0; 1043 | twi_masterBufferLength = ucLength; 1044 | } 1045 | 1046 | void twii_CopyToBuf(uint8_t* pData, uint8_t ucLength) { 1047 | uint8_t i; 1048 | for (i = 0; i < ucLength; ++i) { 1049 | twi_masterBuffer[i] = pData[i]; 1050 | } 1051 | } 1052 | 1053 | void twii_CopyFromBuf(uint8_t *pData, uint8_t ucLength) { 1054 | uint8_t i; 1055 | for (i = 0; i < ucLength; ++i) { 1056 | pData[i] = twi_masterBuffer[i]; 1057 | } 1058 | } 1059 | 1060 | void twii_SetSlaRW(uint8_t ucSlaRW) { 1061 | twi_slarw = ucSlaRW; 1062 | } 1063 | 1064 | void twii_SetStart() { 1065 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA); 1066 | } 1067 | 1068 | void twi_write01() { 1069 | if (TWI_MTX == twi_state) return; // blocking test 1070 | twi_transmitting = 0 ; 1071 | if (twi_error == 0xFF) 1072 | twi_Finish (0); // success 1073 | else if (twi_error == TW_MT_SLA_NACK) 1074 | twi_Finish (2); // error: address send, nack received 1075 | else if (twi_error == TW_MT_DATA_NACK) 1076 | twi_Finish (3); // error: data send, nack received 1077 | else 1078 | twi_Finish (4); // other twi error 1079 | if (twi_cbendTransmissionDone) return twi_cbendTransmissionDone(twi_Return_Value); 1080 | return; 1081 | } 1082 | 1083 | 1084 | void twi_write00() { 1085 | if (TWI_READY != twi_state) return; // blocking test 1086 | if (TWI_BUFFER_LENGTH < ptwv -> length) { 1087 | twi_Finish(1); // end write with error 1 1088 | return; 1089 | } 1090 | twi_Done = 0x00; // show as working 1091 | twii_SetState(TWI_MTX); // to transmitting 1092 | twii_SetError(0xFF); // to No Error 1093 | twii_InitBuffer(0, ptwv -> length); // pointer and length 1094 | twii_CopyToBuf(ptwv -> data, ptwv -> length); // get the data 1095 | twii_SetSlaRW((ptwv -> address << 1) | TW_WRITE); // write command 1096 | twii_SetStart(); // start the cycle 1097 | fNextInterruptFunction = twi_write01; // next routine 1098 | return twi_write01(); 1099 | } 1100 | 1101 | void twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait) { 1102 | uint8_t i; 1103 | ptwv = (twi_Write_Vars *)malloc(sizeof(twi_Write_Vars)); 1104 | ptwv -> address = address; 1105 | ptwv -> data = data; 1106 | ptwv -> length = length; 1107 | ptwv -> wait = wait; 1108 | fNextInterruptFunction = twi_write00; 1109 | return twi_write00(); 1110 | } 1111 | 1112 | void twi_read01() { 1113 | if (TWI_MRX == twi_state) return; // blocking test 1114 | if (twi_masterBufferIndex < ptwv -> length) ptwv -> length = twi_masterBufferIndex; 1115 | twii_CopyFromBuf(ptwv -> data, ptwv -> length); 1116 | twi_Finish(ptwv -> length); 1117 | if (twi_cbreadFromDone) return twi_cbreadFromDone(twi_Return_Value); 1118 | return; 1119 | } 1120 | 1121 | void twi_read00() { 1122 | if (TWI_READY != twi_state) return; // blocking test 1123 | if (TWI_BUFFER_LENGTH < ptwv -> length) twi_Finish(0); // error return 1124 | twi_Done = 0x00; // show as working 1125 | twii_SetState(TWI_MRX); // reading 1126 | twii_SetError(0xFF); // reset error 1127 | twii_InitBuffer(0, ptwv -> length - 1); // init to one less than length 1128 | twii_SetSlaRW((ptwv -> address << 1) | TW_READ); // read command 1129 | twii_SetStart(); // start cycle 1130 | fNextInterruptFunction = twi_read01; 1131 | return twi_read01(); 1132 | } 1133 | 1134 | void twi_readFrom(uint8_t address, uint8_t* data, uint8_t length) { 1135 | uint8_t i; 1136 | 1137 | ptwv = (twi_Write_Vars *)malloc(sizeof(twi_Write_Vars)); 1138 | ptwv -> address = address; 1139 | ptwv -> data = data; 1140 | ptwv -> length = length; 1141 | fNextInterruptFunction = twi_read00; 1142 | return twi_read00(); 1143 | } 1144 | 1145 | void twi_reply(uint8_t ack) { 1146 | // transmit master read ready signal, with or without ack 1147 | if (ack){ 1148 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA); 1149 | } else { 1150 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT); 1151 | } 1152 | } 1153 | 1154 | void twi_stop(void) { 1155 | // send stop condition 1156 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO); 1157 | 1158 | // wait for stop condition to be exectued on bus 1159 | // TWINT is not set after a stop condition! 1160 | while (TWCR & _BV(TWSTO)) { 1161 | continue; 1162 | } 1163 | 1164 | // update twi state 1165 | twi_state = TWI_READY; 1166 | } 1167 | 1168 | void twi_releaseBus(void) { 1169 | // release bus 1170 | TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT); 1171 | 1172 | // update twi state 1173 | twi_state = TWI_READY; 1174 | } 1175 | 1176 | SIGNAL(TWI_vect) { 1177 | switch (TW_STATUS) { 1178 | // All Master 1179 | case TW_START: // sent start condition 1180 | case TW_REP_START: // sent repeated start condition 1181 | // copy device address and r/w bit to output register and ack 1182 | TWDR = twi_slarw; 1183 | twi_reply(1); 1184 | break; 1185 | 1186 | // Master Transmitter 1187 | case TW_MT_SLA_ACK: // slave receiver acked address 1188 | case TW_MT_DATA_ACK: // slave receiver acked data 1189 | // if there is data to send, send it, otherwise stop 1190 | if (twi_masterBufferIndex < twi_masterBufferLength) { 1191 | // copy data to output register and ack 1192 | TWDR = twi_masterBuffer[twi_masterBufferIndex++]; 1193 | twi_reply(1); 1194 | } else { 1195 | twi_stop(); 1196 | } 1197 | break; 1198 | 1199 | case TW_MT_SLA_NACK: // address sent, nack received 1200 | twi_error = TW_MT_SLA_NACK; 1201 | twi_stop(); 1202 | break; 1203 | 1204 | case TW_MT_DATA_NACK: // data sent, nack received 1205 | twi_error = TW_MT_DATA_NACK; 1206 | twi_stop(); 1207 | break; 1208 | 1209 | case TW_MT_ARB_LOST: // lost bus arbitration 1210 | twi_error = TW_MT_ARB_LOST; 1211 | twi_releaseBus(); 1212 | break; 1213 | 1214 | // Master Receiver 1215 | case TW_MR_DATA_ACK: // data received, ack sent 1216 | // put byte into buffer 1217 | twi_masterBuffer[twi_masterBufferIndex++] = TWDR; 1218 | 1219 | case TW_MR_SLA_ACK: // address sent, ack received 1220 | // ack if more bytes are expected, otherwise nack 1221 | if (twi_masterBufferIndex < twi_masterBufferLength) { 1222 | twi_reply(1); 1223 | } else { 1224 | twi_reply(0); 1225 | } 1226 | break; 1227 | 1228 | case TW_MR_DATA_NACK: // data received, nack sent 1229 | // put final byte into buffer 1230 | twi_masterBuffer[twi_masterBufferIndex++] = TWDR; 1231 | 1232 | case TW_MR_SLA_NACK: // address sent, nack received 1233 | twi_stop(); 1234 | break; 1235 | 1236 | // TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case 1237 | 1238 | // Slave Receiver (NOT IMPLEMENTED YET) 1239 | /* 1240 | case TW_SR_SLA_ACK: // addressed, returned ack 1241 | case TW_SR_GCALL_ACK: // addressed generally, returned ack 1242 | case TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ack 1243 | case TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack 1244 | // enter slave receiver mode 1245 | twi_state = TWI_SRX; 1246 | 1247 | // indicate that rx buffer can be overwritten and ack 1248 | twi_rxBufferIndex = 0; 1249 | twi_reply(1); 1250 | break; 1251 | 1252 | case TW_SR_DATA_ACK: // data received, returned ack 1253 | case TW_SR_GCALL_DATA_ACK: // data received generally, returned ack 1254 | // if there is still room in the rx buffer 1255 | if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) { 1256 | // put byte in buffer and ack 1257 | twi_rxBuffer[twi_rxBufferIndex++] = TWDR; 1258 | twi_reply(1); 1259 | } else { 1260 | // otherwise nack 1261 | twi_reply(0); 1262 | } 1263 | break; 1264 | 1265 | case TW_SR_STOP: // stop or repeated start condition received 1266 | // put a null char after data if there's room 1267 | if (twi_rxBufferIndex < TWI_BUFFER_LENGTH) { 1268 | twi_rxBuffer[twi_rxBufferIndex] = 0; 1269 | } 1270 | 1271 | // sends ack and stops interface for clock stretching 1272 | twi_stop(); 1273 | 1274 | // callback to user defined callback 1275 | twi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex); 1276 | 1277 | // since we submit rx buffer to "wire" library, we can reset it 1278 | twi_rxBufferIndex = 0; 1279 | 1280 | // ack future responses and leave slave receiver state 1281 | twi_releaseBus(); 1282 | break; 1283 | 1284 | case TW_SR_DATA_NACK: // data received, returned nack 1285 | case TW_SR_GCALL_DATA_NACK: // data received generally, returned nack 1286 | // nack back at master 1287 | twi_reply(0); 1288 | break; 1289 | 1290 | // Slave Transmitter 1291 | case TW_ST_SLA_ACK: // addressed, returned ack 1292 | case TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack 1293 | // enter slave transmitter mode 1294 | twi_state = TWI_STX; 1295 | 1296 | // ready the tx buffer index for iteration 1297 | twi_txBufferIndex = 0; 1298 | 1299 | // set tx buffer length to be zero, to verify if user changes it 1300 | twi_txBufferLength = 0; 1301 | 1302 | // request for txBuffer to be filled and length to be set 1303 | // note: user must call twi_transmit(bytes, length) to do this 1304 | twi_onSlaveTransmit(); 1305 | 1306 | // if they didn't change buffer & length, initialize it 1307 | if (0 == twi_txBufferLength) { 1308 | twi_txBufferLength = 1; 1309 | twi_txBuffer[0] = 0x00; 1310 | } 1311 | 1312 | // transmit first byte from buffer, fall through 1313 | 1314 | case TW_ST_DATA_ACK: // byte sent, ack returned 1315 | // copy data to output register 1316 | TWDR = twi_txBuffer[twi_txBufferIndex++]; 1317 | 1318 | // if there is more to send, ack, otherwise nack 1319 | if (twi_txBufferIndex < twi_txBufferLength) { 1320 | twi_reply(1); 1321 | } else { 1322 | twi_reply(0); 1323 | } 1324 | break; 1325 | 1326 | case TW_ST_DATA_NACK: // received nack, we are done 1327 | case TW_ST_LAST_DATA: // received ack, but we are done already! 1328 | // ack future responses 1329 | twi_reply(1); 1330 | // leave slave receiver state 1331 | twi_state = TWI_READY; 1332 | break; 1333 | */ 1334 | 1335 | // all 1336 | case TW_NO_INFO: // no state information 1337 | break; 1338 | 1339 | case TW_BUS_ERROR: // bus error, illegal stop/start 1340 | twi_error = TW_BUS_ERROR; 1341 | twi_stop(); 1342 | break; 1343 | } 1344 | 1345 | if (fNextInterruptFunction) return fNextInterruptFunction(); 1346 | } 1347 | 1348 | TwoWire::TwoWire() { } 1349 | 1350 | void TwoWire::begin(void) { 1351 | rxBufferIndex = 0; 1352 | rxBufferLength = 0; 1353 | 1354 | txBufferIndex = 0; 1355 | txBufferLength = 0; 1356 | 1357 | twi_init(); 1358 | } 1359 | 1360 | void TwoWire::beginTransmission(uint8_t address) { 1361 | //beginTransmission((uint8_t)address); 1362 | 1363 | // indicate that we are transmitting 1364 | twi_transmitting = 1; 1365 | 1366 | // set address of targeted slave 1367 | txAddress = address; 1368 | 1369 | // reset tx buffer iterator vars 1370 | txBufferIndex = 0; 1371 | txBufferLength = 0; 1372 | } 1373 | 1374 | uint8_t TwoWire::endTransmission(uint16_t timeout) { 1375 | // transmit buffer (blocking) 1376 | //int8_t ret = 1377 | twi_cbendTransmissionDone = NULL; 1378 | twi_writeTo(txAddress, txBuffer, txBufferLength, 1); 1379 | int8_t ret = twii_WaitForDone(timeout); 1380 | 1381 | // reset tx buffer iterator vars 1382 | txBufferIndex = 0; 1383 | txBufferLength = 0; 1384 | 1385 | // indicate that we are done transmitting 1386 | // twi_transmitting = 0; 1387 | return ret; 1388 | } 1389 | 1390 | void TwoWire::nbendTransmission(void (*function)(int)) { 1391 | twi_cbendTransmissionDone = function; 1392 | twi_writeTo(txAddress, txBuffer, txBufferLength, 1); 1393 | return; 1394 | } 1395 | 1396 | void TwoWire::send(uint8_t data) { 1397 | if (twi_transmitting) { 1398 | // in master transmitter mode 1399 | // don't bother if buffer is full 1400 | if (txBufferLength >= NBWIRE_BUFFER_LENGTH) { 1401 | return; 1402 | } 1403 | 1404 | // put byte in tx buffer 1405 | txBuffer[txBufferIndex] = data; 1406 | ++txBufferIndex; 1407 | 1408 | // update amount in buffer 1409 | txBufferLength = txBufferIndex; 1410 | } else { 1411 | // in slave send mode 1412 | // reply to master 1413 | //twi_transmit(&data, 1); 1414 | } 1415 | } 1416 | 1417 | uint8_t TwoWire::receive(void) { 1418 | // default to returning null char 1419 | // for people using with char strings 1420 | uint8_t value = 0; 1421 | 1422 | // get each successive byte on each call 1423 | if (rxBufferIndex < rxBufferLength) { 1424 | value = rxBuffer[rxBufferIndex]; 1425 | ++rxBufferIndex; 1426 | } 1427 | 1428 | return value; 1429 | } 1430 | 1431 | uint8_t TwoWire::requestFrom(uint8_t address, int quantity, uint16_t timeout) { 1432 | // clamp to buffer length 1433 | if (quantity > NBWIRE_BUFFER_LENGTH) { 1434 | quantity = NBWIRE_BUFFER_LENGTH; 1435 | } 1436 | 1437 | // perform blocking read into buffer 1438 | twi_cbreadFromDone = NULL; 1439 | twi_readFrom(address, rxBuffer, quantity); 1440 | uint8_t read = twii_WaitForDone(timeout); 1441 | 1442 | // set rx buffer iterator vars 1443 | rxBufferIndex = 0; 1444 | rxBufferLength = read; 1445 | 1446 | return read; 1447 | } 1448 | 1449 | void TwoWire::nbrequestFrom(uint8_t address, int quantity, void (*function)(int)) { 1450 | // clamp to buffer length 1451 | if (quantity > NBWIRE_BUFFER_LENGTH) { 1452 | quantity = NBWIRE_BUFFER_LENGTH; 1453 | } 1454 | 1455 | // perform blocking read into buffer 1456 | twi_cbreadFromDone = function; 1457 | twi_readFrom(address, rxBuffer, quantity); 1458 | //uint8_t read = twii_WaitForDone(); 1459 | 1460 | // set rx buffer iterator vars 1461 | //rxBufferIndex = 0; 1462 | //rxBufferLength = read; 1463 | 1464 | rxBufferIndex = 0; 1465 | rxBufferLength = quantity; // this is a hack 1466 | 1467 | return; //read; 1468 | } 1469 | 1470 | uint8_t TwoWire::available(void) { 1471 | return rxBufferLength - rxBufferIndex; 1472 | } 1473 | 1474 | #endif 1475 | -------------------------------------------------------------------------------- /lib/I2Cdev/I2Cdev.h: -------------------------------------------------------------------------------- 1 | // I2Cdev library collection - Main I2C device class header file 2 | // Abstracts bit and byte I2C R/W functions into a convenient class 3 | // 2013-06-05 by Jeff Rowberg 4 | // 5 | // Changelog: 6 | // 2020-01-20 - hardija : complete support for Teensy 3.x 7 | // 2015-10-30 - simondlevy : support i2c_t3 for Teensy3.1 8 | // 2013-05-06 - add Francesco Ferrara's Fastwire v0.24 implementation with small modifications 9 | // 2013-05-05 - fix issue with writing bit values to words (Sasquatch/Farzanegan) 10 | // 2012-06-09 - fix major issue with reading > 32 bytes at a time with Arduino Wire 11 | // - add compiler warnings when using outdated or IDE or limited I2Cdev implementation 12 | // 2011-11-01 - fix write*Bits mask calculation (thanks sasquatch @ Arduino forums) 13 | // 2011-10-03 - added automatic Arduino version detection for ease of use 14 | // 2011-10-02 - added Gene Knight's NBWire TwoWire class implementation with small modifications 15 | // 2011-08-31 - added support for Arduino 1.0 Wire library (methods are different from 0.x) 16 | // 2011-08-03 - added optional timeout parameter to read* methods to easily change from default 17 | // 2011-08-02 - added support for 16-bit registers 18 | // - fixed incorrect Doxygen comments on some methods 19 | // - added timeout value for read operations (thanks mem @ Arduino forums) 20 | // 2011-07-30 - changed read/write function structures to return success or byte counts 21 | // - made all methods static for multi-device memory savings 22 | // 2011-07-28 - initial release 23 | 24 | /* ============================================ 25 | I2Cdev device library code is placed under the MIT license 26 | Copyright (c) 2013 Jeff Rowberg 27 | 28 | Permission is hereby granted, free of charge, to any person obtaining a copy 29 | of this software and associated documentation files (the "Software"), to deal 30 | in the Software without restriction, including without limitation the rights 31 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 32 | copies of the Software, and to permit persons to whom the Software is 33 | furnished to do so, subject to the following conditions: 34 | 35 | The above copyright notice and this permission notice shall be included in 36 | all copies or substantial portions of the Software. 37 | 38 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 39 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 40 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 41 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 42 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 43 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 44 | THE SOFTWARE. 45 | =============================================== 46 | */ 47 | 48 | #ifndef _I2CDEV_H_ 49 | #define _I2CDEV_H_ 50 | 51 | // ----------------------------------------------------------------------------- 52 | // I2C interface implementation setting 53 | // ----------------------------------------------------------------------------- 54 | #ifndef I2CDEV_IMPLEMENTATION 55 | #define I2CDEV_IMPLEMENTATION I2CDEV_ARDUINO_WIRE 56 | //#define I2CDEV_IMPLEMENTATION I2CDEV_TEENSY_3X_WIRE 57 | //#define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_SBWIRE 58 | //#define I2CDEV_IMPLEMENTATION I2CDEV_BUILTIN_FASTWIRE 59 | #endif // I2CDEV_IMPLEMENTATION 60 | 61 | // comment this out if you are using a non-optimal IDE/implementation setting 62 | // but want the compiler to shut up about it 63 | #define I2CDEV_IMPLEMENTATION_WARNINGS 64 | 65 | // ----------------------------------------------------------------------------- 66 | // I2C interface implementation options 67 | // ----------------------------------------------------------------------------- 68 | #define I2CDEV_ARDUINO_WIRE 1 // Wire object from Arduino 69 | #define I2CDEV_BUILTIN_NBWIRE 2 // Tweaked Wire object from Gene Knight's NBWire project 70 | // ^^^ NBWire implementation is still buggy w/some interrupts! 71 | #define I2CDEV_BUILTIN_FASTWIRE 3 // FastWire object from Francesco Ferrara's project 72 | #define I2CDEV_I2CMASTER_LIBRARY 4 // I2C object from DSSCircuits I2C-Master Library at https://github.com/DSSCircuits/I2C-Master-Library 73 | #define I2CDEV_BUILTIN_SBWIRE 5 // I2C object from Shuning (Steve) Bian's SBWire Library at https://github.com/freespace/SBWire 74 | #define I2CDEV_TEENSY_3X_WIRE 6 // Teensy 3.x support using i2c_t3 library 75 | 76 | // ----------------------------------------------------------------------------- 77 | // Arduino-style "Serial.print" debug constant (uncomment to enable) 78 | // ----------------------------------------------------------------------------- 79 | //#define I2CDEV_SERIAL_DEBUG 80 | 81 | #ifdef ARDUINO 82 | #if ARDUINO < 100 83 | #include "WProgram.h" 84 | #else 85 | #include "Arduino.h" 86 | #endif 87 | #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE 88 | #include 89 | #endif 90 | #if I2CDEV_IMPLEMENTATION == I2CDEV_TEENSY_3X_WIRE 91 | #include 92 | #endif 93 | #if I2CDEV_IMPLEMENTATION == I2CDEV_I2CMASTER_LIBRARY 94 | #include 95 | #endif 96 | #if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_SBWIRE 97 | #include "SBWire.h" 98 | #endif 99 | #endif 100 | 101 | #ifdef SPARK 102 | #include 103 | #define ARDUINO 101 104 | #endif 105 | 106 | 107 | // 1000ms default read timeout (modify with "I2Cdev::readTimeout = [ms];") 108 | #define I2CDEV_DEFAULT_READ_TIMEOUT 1000 109 | 110 | class I2Cdev { 111 | public: 112 | I2Cdev(); 113 | 114 | static int8_t readBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); 115 | static int8_t readBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); 116 | static int8_t readBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); 117 | static int8_t readBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); 118 | static int8_t readByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); 119 | static int8_t readWord(uint8_t devAddr, uint8_t regAddr, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); 120 | static int8_t readBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data, uint16_t timeout=I2Cdev::readTimeout); 121 | static int8_t readWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data, uint16_t timeout=I2Cdev::readTimeout); 122 | 123 | static bool writeBit(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint8_t data); 124 | static bool writeBitW(uint8_t devAddr, uint8_t regAddr, uint8_t bitNum, uint16_t data); 125 | static bool writeBits(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint8_t data); 126 | static bool writeBitsW(uint8_t devAddr, uint8_t regAddr, uint8_t bitStart, uint8_t length, uint16_t data); 127 | static bool writeByte(uint8_t devAddr, uint8_t regAddr, uint8_t data); 128 | static bool writeWord(uint8_t devAddr, uint8_t regAddr, uint16_t data); 129 | static bool writeBytes(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint8_t *data); 130 | static bool writeWords(uint8_t devAddr, uint8_t regAddr, uint8_t length, uint16_t *data); 131 | 132 | static uint16_t readTimeout; 133 | }; 134 | 135 | #if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE 136 | ////////////////////// 137 | // FastWire 0.24 138 | // This is a library to help faster programs to read I2C devices. 139 | // Copyright(C) 2012 140 | // Francesco Ferrara 141 | ////////////////////// 142 | 143 | /* Master */ 144 | #define TW_START 0x08 145 | #define TW_REP_START 0x10 146 | 147 | /* Master Transmitter */ 148 | #define TW_MT_SLA_ACK 0x18 149 | #define TW_MT_SLA_NACK 0x20 150 | #define TW_MT_DATA_ACK 0x28 151 | #define TW_MT_DATA_NACK 0x30 152 | #define TW_MT_ARB_LOST 0x38 153 | 154 | /* Master Receiver */ 155 | #define TW_MR_ARB_LOST 0x38 156 | #define TW_MR_SLA_ACK 0x40 157 | #define TW_MR_SLA_NACK 0x48 158 | #define TW_MR_DATA_ACK 0x50 159 | #define TW_MR_DATA_NACK 0x58 160 | 161 | #define TW_OK 0 162 | #define TW_ERROR 1 163 | 164 | class Fastwire { 165 | private: 166 | static boolean waitInt(); 167 | 168 | public: 169 | static void setup(int khz, boolean pullup); 170 | static byte beginTransmission(byte device); 171 | static byte write(byte value); 172 | static byte writeBuf(byte device, byte address, byte *data, byte num); 173 | static byte readBuf(byte device, byte address, byte *data, byte num); 174 | static void reset(); 175 | static byte stop(); 176 | }; 177 | #endif 178 | 179 | #if I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE 180 | // NBWire implementation based heavily on code by Gene Knight 181 | // Originally posted on the Arduino forum at http://arduino.cc/forum/index.php/topic,70705.0.html 182 | // Originally offered to the i2cdevlib project at http://arduino.cc/forum/index.php/topic,68210.30.html 183 | 184 | #define NBWIRE_BUFFER_LENGTH 32 185 | 186 | class TwoWire { 187 | private: 188 | static uint8_t rxBuffer[]; 189 | static uint8_t rxBufferIndex; 190 | static uint8_t rxBufferLength; 191 | 192 | static uint8_t txAddress; 193 | static uint8_t txBuffer[]; 194 | static uint8_t txBufferIndex; 195 | static uint8_t txBufferLength; 196 | 197 | // static uint8_t transmitting; 198 | static void (*user_onRequest)(void); 199 | static void (*user_onReceive)(int); 200 | static void onRequestService(void); 201 | static void onReceiveService(uint8_t*, int); 202 | 203 | public: 204 | TwoWire(); 205 | void begin(); 206 | void begin(uint8_t); 207 | void begin(int); 208 | void beginTransmission(uint8_t); 209 | //void beginTransmission(int); 210 | uint8_t endTransmission(uint16_t timeout=0); 211 | void nbendTransmission(void (*function)(int)) ; 212 | uint8_t requestFrom(uint8_t, int, uint16_t timeout=0); 213 | //uint8_t requestFrom(int, int); 214 | void nbrequestFrom(uint8_t, int, void (*function)(int)); 215 | void send(uint8_t); 216 | void send(uint8_t*, uint8_t); 217 | //void send(int); 218 | void send(char*); 219 | uint8_t available(void); 220 | uint8_t receive(void); 221 | void onReceive(void (*)(int)); 222 | void onRequest(void (*)(void)); 223 | }; 224 | 225 | #define TWI_READY 0 226 | #define TWI_MRX 1 227 | #define TWI_MTX 2 228 | #define TWI_SRX 3 229 | #define TWI_STX 4 230 | 231 | #define TW_WRITE 0 232 | #define TW_READ 1 233 | 234 | #define TW_MT_SLA_NACK 0x20 235 | #define TW_MT_DATA_NACK 0x30 236 | 237 | #define CPU_FREQ 16000000L 238 | #define TWI_FREQ 100000L 239 | #define TWI_BUFFER_LENGTH 32 240 | 241 | /* TWI Status is in TWSR, in the top 5 bits: TWS7 - TWS3 */ 242 | 243 | #define TW_STATUS_MASK (_BV(TWS7)|_BV(TWS6)|_BV(TWS5)|_BV(TWS4)|_BV(TWS3)) 244 | #define TW_STATUS (TWSR & TW_STATUS_MASK) 245 | #define TW_START 0x08 246 | #define TW_REP_START 0x10 247 | #define TW_MT_SLA_ACK 0x18 248 | #define TW_MT_SLA_NACK 0x20 249 | #define TW_MT_DATA_ACK 0x28 250 | #define TW_MT_DATA_NACK 0x30 251 | #define TW_MT_ARB_LOST 0x38 252 | #define TW_MR_ARB_LOST 0x38 253 | #define TW_MR_SLA_ACK 0x40 254 | #define TW_MR_SLA_NACK 0x48 255 | #define TW_MR_DATA_ACK 0x50 256 | #define TW_MR_DATA_NACK 0x58 257 | #define TW_ST_SLA_ACK 0xA8 258 | #define TW_ST_ARB_LOST_SLA_ACK 0xB0 259 | #define TW_ST_DATA_ACK 0xB8 260 | #define TW_ST_DATA_NACK 0xC0 261 | #define TW_ST_LAST_DATA 0xC8 262 | #define TW_SR_SLA_ACK 0x60 263 | #define TW_SR_ARB_LOST_SLA_ACK 0x68 264 | #define TW_SR_GCALL_ACK 0x70 265 | #define TW_SR_ARB_LOST_GCALL_ACK 0x78 266 | #define TW_SR_DATA_ACK 0x80 267 | #define TW_SR_DATA_NACK 0x88 268 | #define TW_SR_GCALL_DATA_ACK 0x90 269 | #define TW_SR_GCALL_DATA_NACK 0x98 270 | #define TW_SR_STOP 0xA0 271 | #define TW_NO_INFO 0xF8 272 | #define TW_BUS_ERROR 0x00 273 | 274 | //#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr)) 275 | //#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr)) 276 | 277 | #ifndef sbi // set bit 278 | #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) 279 | #endif // sbi 280 | 281 | #ifndef cbi // clear bit 282 | #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) 283 | #endif // cbi 284 | 285 | extern TwoWire Wire; 286 | 287 | #endif // I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_NBWIRE 288 | 289 | #endif /* _I2CDEV_H_ */ 290 | -------------------------------------------------------------------------------- /lib/I2Cdev/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For I2Cdev 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | I2Cdev KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | 14 | readBit KEYWORD2 15 | readBitW KEYWORD2 16 | readBits KEYWORD2 17 | readBitsW KEYWORD2 18 | readByte KEYWORD2 19 | readBytes KEYWORD2 20 | readWord KEYWORD2 21 | readWords KEYWORD2 22 | writeBit KEYWORD2 23 | writeBitW KEYWORD2 24 | writeBits KEYWORD2 25 | writeBitsW KEYWORD2 26 | writeByte KEYWORD2 27 | writeBytes KEYWORD2 28 | writeWord KEYWORD2 29 | writeWords KEYWORD2 30 | 31 | ####################################### 32 | # Instances (KEYWORD2) 33 | ####################################### 34 | 35 | ####################################### 36 | # Constants (LITERAL1) 37 | ####################################### 38 | 39 | -------------------------------------------------------------------------------- /lib/I2Cdev/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "I2Cdevlib-Core", 3 | "keywords": "i2cdevlib, i2c", 4 | "description": "The I2C Device Library (I2Cdevlib) is a collection of uniform and well-documented classes to provide simple and intuitive interfaces to I2C devices.", 5 | "include": "Arduino/I2Cdev", 6 | "repository": 7 | { 8 | "type": "git", 9 | "url": "https://github.com/jrowberg/i2cdevlib.git" 10 | }, 11 | "frameworks": "arduino", 12 | "platforms": "atmelavr", 13 | "dependencies": [ 14 | { 15 | "name": "Wire" 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /lib/KnobHelper/KnobHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "KnobHelper.h" 2 | AiEsp32RotaryEncoder rotary = AiEsp32RotaryEncoder(ROTARY_ENCODER_A_PIN, ROTARY_ENCODER_B_PIN, ROTARY_ENCODER_BUTTON_PIN, ROTARY_ENCODER_VCC_PIN); 3 | 4 | void KnobHelperClass::read() { 5 | preferences.begin("knob", true); 6 | host = preferences.getString("host"); 7 | port = preferences.getInt("port", 3333); 8 | preferences.end(); 9 | } 10 | 11 | void KnobHelperClass::write() { 12 | preferences.begin("knob", false); 13 | preferences.putString("host", host); 14 | preferences.putInt("port", port); 15 | preferences.end(); 16 | } 17 | 18 | void KnobHelperClass::setup() { 19 | unsigned long setupStartTime = millis(); 20 | Serial.println("Setup Knob helper"); 21 | 22 | read(); 23 | 24 | // initialize rotary encoder 25 | rotary.begin(); 26 | rotary.setup([] { rotary.readEncoder_ISR(); }); 27 | rotary.setBoundaries(0, 100, false); 28 | 29 | setupDuration = millis() - setupStartTime; 30 | Serial.print("Setup knob helper took "); 31 | Serial.println(setupDuration); 32 | } 33 | 34 | void KnobHelperClass::loop() { 35 | readValues(); 36 | sendValues(); 37 | } 38 | 39 | void KnobHelperClass::sleep() { 40 | } 41 | 42 | void KnobHelperClass::readValues() { 43 | prevValue = value; 44 | pressed = false; 45 | 46 | // first lets handle rotary encoder button click 47 | if (rotary.currentButtonState() == BUT_RELEASED) { 48 | pressed = true; 49 | Serial.println("Button Pressed"); 50 | } 51 | 52 | // lets see if anything changed 53 | // optionally we can ignore whenever there is no change 54 | int16_t encoderDelta = rotary.encoderChanged(); 55 | if (encoderDelta == 0) return; 56 | 57 | // if value is changed compared to our last read 58 | if (encoderDelta != 0) { 59 | // now we need current value 60 | value = rotary.readEncoder(); 61 | 62 | Serial.print("Value: "); 63 | Serial.println(value); 64 | } 65 | } 66 | 67 | void KnobHelperClass::sendValues() { 68 | if (value == prevValue && !pressed) return; 69 | if (host.length() == 0) return; 70 | if (!WiFiHelper.connect()) return; 71 | unsigned long requestStartTime = millis(); 72 | 73 | StaticJsonDocument<32> doc; 74 | 75 | udp.beginPacket(host.c_str(), port); 76 | 77 | doc["value"] = value; 78 | doc["pressed"] = pressed; 79 | serializeJson(doc, udp); 80 | 81 | udp.println(); 82 | udp.endPacket(); 83 | 84 | requestDuration = millis() - requestStartTime; 85 | Serial.print("Send UDP request "); 86 | Serial.println(requestDuration); 87 | } 88 | 89 | void KnobHelperClass::server() { 90 | Serial.println("Setup Knob server"); 91 | 92 | WebServerHelper.server.on("/api/knob", HTTP_GET, [this](AsyncWebServerRequest *request) { 93 | int args = request->args(); 94 | 95 | if (args > 0) { 96 | request->send(200, "text/plain", "message received"); 97 | Serial.println("Update knob settings"); 98 | 99 | if (request->hasArg("host")) host = request->arg("host"); 100 | if (request->hasArg("port")) port = request->arg("port").toInt(); 101 | 102 | write(); 103 | 104 | } else { 105 | AsyncJsonResponse *response = new AsyncJsonResponse(); 106 | response->addHeader("Server", "ESP Async Web Server"); 107 | JsonVariant &root = response->getRoot(); 108 | 109 | root["value"] = value; 110 | root["setupDuration"] = setupDuration; 111 | root["requestDuration"] = requestDuration; 112 | 113 | root["host"] = host; 114 | root["port"] = port; 115 | 116 | response->setLength(); 117 | request->send(response); 118 | } 119 | }); 120 | } 121 | 122 | KnobHelperClass KnobHelper; 123 | -------------------------------------------------------------------------------- /lib/KnobHelper/KnobHelper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef KnobHelper_h 3 | #define KnobHelper_h 4 | 5 | #include "AiEsp32RotaryEncoder.h" 6 | #include "ArduinoJson.h" 7 | #include "AsyncJson.h" 8 | #include "I2Cdev.h" 9 | #include "MPU6050.h" 10 | #include "Preferences.h" 11 | #include "WebServerHelper.h" 12 | #include "WiFiHelper.h" 13 | #include "WiFiUdp.h" 14 | #include "Wire.h" 15 | 16 | /* 17 | connecting Rotary encoder 18 | CLK (A pin) - to any microcontroler intput pin with interrupt -> in this example pin 32 19 | DT (B pin) - to any microcontroler intput pin with interrupt -> in this example pin 21 20 | SW (button pin) - to any microcontroler intput pin -> in this example pin 25 21 | VCC - to microcontroler VCC (then set ROTARY_ENCODER_VCC_PIN -1) or in this example pin 25 22 | GND - to microcontroler GND 23 | */ 24 | #define ROTARY_ENCODER_A_PIN 15 25 | #define ROTARY_ENCODER_B_PIN 4 26 | #define ROTARY_ENCODER_BUTTON_PIN 13 27 | #define ROTARY_ENCODER_VCC_PIN -1 /*put -1 of Rotary encoder Vcc is connected directly to 3,3V; else you can use declared output pin for powering rotary encoder */ 28 | #define ROTARY_ENCODER_TEST_LIMITS = 2; 29 | 30 | class KnobHelperClass { 31 | private: 32 | WiFiUDP udp; 33 | Preferences preferences; 34 | 35 | String host; 36 | int32_t port; 37 | 38 | void read(); 39 | void write(); 40 | 41 | void readValues(); 42 | void sendValues(); 43 | 44 | unsigned long requestDuration; 45 | unsigned long setupDuration; 46 | 47 | int threshold; 48 | int testlimits = 2; 49 | 50 | public: 51 | int16_t value; 52 | int16_t prevValue; 53 | bool pressed; 54 | 55 | void setup(); 56 | void server(); 57 | void loop(); 58 | void sleep(); 59 | }; 60 | 61 | extern KnobHelperClass KnobHelper; 62 | 63 | #endif -------------------------------------------------------------------------------- /lib/KnobHelper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "KnobHelper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /lib/LedHelper/LedHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "LedHelper.h" 2 | 3 | void LedHelperClass::read() { 4 | preferences.begin("Led", true); 5 | mode = preferences.getInt("mode", 0); 6 | brightness = preferences.getInt("brightness", 100); 7 | color = preferences.getInt("color", 0x808000); 8 | minValue = preferences.getInt("minValue", 25); 9 | maxValue = preferences.getInt("maxValue", 350); 10 | initHue = preferences.getInt("initHue", 96); 11 | endHue = preferences.getInt("endHue", 0); 12 | deltaHue = preferences.getInt("deltaHue", -4); 13 | preferences.end(); 14 | } 15 | 16 | void LedHelperClass::write() { 17 | preferences.begin("Led", false); 18 | preferences.putInt("mode", mode); 19 | preferences.putInt("brightness", brightness); 20 | preferences.putInt("color", color); 21 | preferences.putInt("minValue", minValue); 22 | preferences.putInt("maxValue", maxValue); 23 | preferences.putInt("initHue", initHue); 24 | preferences.putInt("deltaHue", deltaHue); 25 | preferences.end(); 26 | } 27 | 28 | void LedHelperClass::setup() { 29 | read(); 30 | value = 0; 31 | displayValue = 0; 32 | FastLED.addLeds(leds, NUM_LEDS); 33 | FastLED.setBrightness(brightness); 34 | } 35 | 36 | int LedHelperClass::valueToNumLed() { 37 | int numLedsToLight = map(displayValue, minValue, maxValue, 0, NUM_LEDS); 38 | numLedsToLight = max(0, min(numLedsToLight, NUM_LEDS)); 39 | return numLedsToLight; 40 | } 41 | 42 | int LedHelperClass::valueToHue() { 43 | int hue = map(displayValue, minValue, maxValue, initHue, endHue); 44 | 45 | return hue; 46 | } 47 | 48 | void LedHelperClass::fillColorsFromPalette(uint8_t colorIndex) { 49 | uint8_t brightness = 255; 50 | 51 | for (int i = 0; i < NUM_LEDS; i++) { 52 | leds[i] = ColorFromPalette(currentPalette, colorIndex, brightness, currentBlending); 53 | // TODO adjustable 54 | colorIndex += 3; 55 | } 56 | } 57 | 58 | bool LedHelperClass::animate() { 59 | switch (animation) { 60 | case 1: 61 | currentPalette = RainbowColors_p; 62 | currentBlending = LINEARBLEND; 63 | break; 64 | case 2: 65 | currentPalette = RainbowStripeColors_p; 66 | currentBlending = NOBLEND; 67 | break; 68 | case 3: 69 | currentPalette = CloudColors_p; 70 | currentBlending = LINEARBLEND; 71 | break; 72 | case 4: 73 | currentPalette = PartyColors_p; 74 | currentBlending = LINEARBLEND; 75 | break; 76 | case 5: 77 | currentPalette = OceanColors_p; 78 | currentBlending = LINEARBLEND; 79 | break; 80 | case 6: 81 | currentPalette = HeatColors_p; 82 | currentBlending = LINEARBLEND; 83 | break; 84 | case 7: 85 | currentPalette = ForestColors_p; 86 | currentBlending = LINEARBLEND; 87 | break; 88 | case 8: 89 | currentPalette = LavaColors_p; 90 | currentBlending = LINEARBLEND; 91 | break; 92 | 93 | default: 94 | return false; 95 | } 96 | 97 | static uint8_t startIndex = 0; 98 | // TODO adjustable 99 | startIndex = startIndex + 1; /* motion speed */ 100 | 101 | fillColorsFromPalette(startIndex); 102 | 103 | FastLED.show(); 104 | FastLED.delay(1000 / 100); 105 | 106 | return true; 107 | } 108 | 109 | void LedHelperClass::loop() { 110 | value = max(0, min(maxValue, value)); 111 | 112 | if (test > 0) { 113 | if (test == 1) { 114 | value = maxValue; 115 | test++; 116 | } else { 117 | value = 0; 118 | test = 0; 119 | } 120 | } 121 | 122 | if (animate()) return; 123 | 124 | int startValue = displayValue; 125 | int range = abs(displayValue - value); 126 | for (int i = 0; i < range; i++) { 127 | fract8 f = (256 * i) / range; 128 | fract8 easeOutVal = ease8InOutQuad(f); 129 | displayValue = lerp16by8(startValue, value, easeOutVal); 130 | 131 | FastLED.clear(); 132 | switch (mode) { 133 | case 0: { 134 | fill_rainbow(leds, valueToNumLed(), initHue, deltaHue); 135 | break; 136 | } 137 | case 1: { 138 | fill_solid(leds, valueToNumLed(), color); 139 | break; 140 | } 141 | case 2: { 142 | fill_solid(leds, NUM_LEDS, displayValue < minValue ? CHSV(0, 0, 0) : CHSV(valueToHue(), 255, 255)); 143 | break; 144 | } 145 | default: { 146 | break; 147 | } 148 | } 149 | 150 | if (brightness == -1) { 151 | FastLED.setBrightness(map(displayValue, minValue, maxValue, 0, 255)); 152 | } else { 153 | FastLED.setBrightness(brightness); 154 | } 155 | 156 | FastLED.show(); 157 | FastLED.delay(5); 158 | } 159 | 160 | displayValue = value; 161 | } 162 | 163 | void LedHelperClass::sleep() { 164 | } 165 | 166 | void LedHelperClass::server() { 167 | Serial.println("Setup Led server"); 168 | 169 | WebServerHelper.server.on("/api/led", HTTP_GET, [this](AsyncWebServerRequest *request) { 170 | int args = request->args(); 171 | 172 | if (args > 0) { 173 | request->send(200, "text/plain", "message received"); 174 | Serial.println("Update Led settings"); 175 | 176 | if (request->hasArg("value")) value = request->arg("value").toInt(); 177 | if (request->hasArg("brightness")) brightness = request->arg("brightness").toInt(); 178 | if (request->hasArg("mode")) mode = request->arg("mode").toInt(); 179 | if (request->hasArg("color")) color = request->arg("color").toInt(); 180 | if (request->hasArg("min")) minValue = request->arg("min").toInt(); 181 | if (request->hasArg("max")) maxValue = request->arg("max").toInt(); 182 | if (request->hasArg("initHue")) initHue = request->arg("initHue").toInt(); 183 | if (request->hasArg("deltaHue")) deltaHue = request->arg("deltaHue").toInt(); 184 | if (request->hasArg("endHue")) endHue = request->arg("endHue").toInt(); 185 | if (request->hasArg("animation")) animation = request->arg("animation").toInt(); 186 | 187 | test = 1; 188 | write(); 189 | 190 | } else { 191 | AsyncJsonResponse *response = new AsyncJsonResponse(); 192 | response->addHeader("Server", "ESP Async Web Server"); 193 | JsonVariant &root = response->getRoot(); 194 | 195 | root["value"] = value; 196 | root["brightness"] = brightness; 197 | root["mode"] = mode; 198 | root["color"] = color; 199 | root["min"] = minValue; 200 | root["max"] = maxValue; 201 | root["initHue"] = initHue; 202 | root["deltaHue"] = deltaHue; 203 | root["endHue"] = endHue; 204 | root["animation"] = animation; 205 | 206 | response->setLength(); 207 | request->send(response); 208 | } 209 | }); 210 | } 211 | 212 | LedHelperClass LedHelper; 213 | -------------------------------------------------------------------------------- /lib/LedHelper/LedHelper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef LedHelper_h 3 | #define LedHelper_h 4 | 5 | #include "Arduino.h" 6 | #include "ArduinoJson.h" 7 | #include "AsyncJson.h" 8 | #include "FastLED.h" 9 | #include "Preferences.h" 10 | #include "WebServerHelper.h" 11 | 12 | #define DATA_PIN 13 13 | #define NUM_LEDS 24 14 | #define LED_TYPE WS2812 15 | #define COLOR_ORDER GRB 16 | 17 | class LedHelperClass { 18 | private: 19 | Preferences preferences; 20 | CRGB leds[NUM_LEDS]; 21 | 22 | int mode; 23 | int color; 24 | int brightness; 25 | int initHue; 26 | int deltaHue; 27 | int endHue; 28 | int displayValue; 29 | 30 | void read(); 31 | void write(); 32 | 33 | int valueToNumLed(); 34 | int valueToHue(); 35 | 36 | bool animate(); 37 | void fillColorsFromPalette(uint8_t colorIndex); 38 | 39 | CRGBPalette16 currentPalette; 40 | TBlendType currentBlending; 41 | 42 | public: 43 | int value; 44 | int minValue; 45 | int maxValue; 46 | int test; 47 | int animation; 48 | 49 | void setup(); 50 | void server(); 51 | void loop(); 52 | void sleep(); 53 | }; 54 | 55 | extern LedHelperClass LedHelper; 56 | 57 | #endif -------------------------------------------------------------------------------- /lib/LedHelper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LedHelper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /lib/MPU6050/MPU6050.h: -------------------------------------------------------------------------------- 1 | // I2Cdev library collection - MPU6050 I2C device class 2 | // Based on InvenSense MPU-6050 register map document rev. 2.0, 5/19/2011 (RM-MPU-6000A-00) 3 | // 10/3/2011 by Jeff Rowberg 4 | // Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib 5 | // 6 | // Changelog: 7 | // ... - ongoing debug release 8 | 9 | // NOTE: THIS IS ONLY A PARIAL RELEASE. THIS DEVICE CLASS IS CURRENTLY UNDERGOING ACTIVE 10 | // DEVELOPMENT AND IS STILL MISSING SOME IMPORTANT FEATURES. PLEASE KEEP THIS IN MIND IF 11 | // YOU DECIDE TO USE THIS PARTICULAR CODE FOR ANYTHING. 12 | 13 | /* ============================================ 14 | I2Cdev device library code is placed under the MIT license 15 | Copyright (c) 2012 Jeff Rowberg 16 | 17 | Permission is hereby granted, free of charge, to any person obtaining a copy 18 | of this software and associated documentation files (the "Software"), to deal 19 | in the Software without restriction, including without limitation the rights 20 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 21 | copies of the Software, and to permit persons to whom the Software is 22 | furnished to do so, subject to the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included in 25 | all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 28 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 31 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 33 | THE SOFTWARE. 34 | =============================================== 35 | */ 36 | 37 | #ifndef _MPU6050_H_ 38 | #define _MPU6050_H_ 39 | 40 | #include "I2Cdev.h" 41 | 42 | // supporting link: http://forum.arduino.cc/index.php?&topic=143444.msg1079517#msg1079517 43 | // also: http://forum.arduino.cc/index.php?&topic=141571.msg1062899#msg1062899s 44 | 45 | #ifdef __AVR__ 46 | #include 47 | #else 48 | //#define PROGMEM /* empty */ 49 | //#define pgm_read_byte(x) (*(x)) 50 | //#define pgm_read_word(x) (*(x)) 51 | //#define pgm_read_float(x) (*(x)) 52 | //#define PSTR(STR) STR 53 | #endif 54 | 55 | 56 | #define MPU6050_ADDRESS_AD0_LOW 0x68 // address pin low (GND), default for InvenSense evaluation board 57 | #define MPU6050_ADDRESS_AD0_HIGH 0x69 // address pin high (VCC) 58 | #define MPU6050_DEFAULT_ADDRESS MPU6050_ADDRESS_AD0_LOW 59 | 60 | #define MPU6050_RA_XG_OFFS_TC 0x00 //[7] PWR_MODE, [6:1] XG_OFFS_TC, [0] OTP_BNK_VLD 61 | #define MPU6050_RA_YG_OFFS_TC 0x01 //[7] PWR_MODE, [6:1] YG_OFFS_TC, [0] OTP_BNK_VLD 62 | #define MPU6050_RA_ZG_OFFS_TC 0x02 //[7] PWR_MODE, [6:1] ZG_OFFS_TC, [0] OTP_BNK_VLD 63 | #define MPU6050_RA_X_FINE_GAIN 0x03 //[7:0] X_FINE_GAIN 64 | #define MPU6050_RA_Y_FINE_GAIN 0x04 //[7:0] Y_FINE_GAIN 65 | #define MPU6050_RA_Z_FINE_GAIN 0x05 //[7:0] Z_FINE_GAIN 66 | #define MPU6050_RA_XA_OFFS_H 0x06 //[15:0] XA_OFFS 67 | #define MPU6050_RA_XA_OFFS_L_TC 0x07 68 | #define MPU6050_RA_YA_OFFS_H 0x08 //[15:0] YA_OFFS 69 | #define MPU6050_RA_YA_OFFS_L_TC 0x09 70 | #define MPU6050_RA_ZA_OFFS_H 0x0A //[15:0] ZA_OFFS 71 | #define MPU6050_RA_ZA_OFFS_L_TC 0x0B 72 | #define MPU6050_RA_SELF_TEST_X 0x0D //[7:5] XA_TEST[4-2], [4:0] XG_TEST[4-0] 73 | #define MPU6050_RA_SELF_TEST_Y 0x0E //[7:5] YA_TEST[4-2], [4:0] YG_TEST[4-0] 74 | #define MPU6050_RA_SELF_TEST_Z 0x0F //[7:5] ZA_TEST[4-2], [4:0] ZG_TEST[4-0] 75 | #define MPU6050_RA_SELF_TEST_A 0x10 //[5:4] XA_TEST[1-0], [3:2] YA_TEST[1-0], [1:0] ZA_TEST[1-0] 76 | #define MPU6050_RA_XG_OFFS_USRH 0x13 //[15:0] XG_OFFS_USR 77 | #define MPU6050_RA_XG_OFFS_USRL 0x14 78 | #define MPU6050_RA_YG_OFFS_USRH 0x15 //[15:0] YG_OFFS_USR 79 | #define MPU6050_RA_YG_OFFS_USRL 0x16 80 | #define MPU6050_RA_ZG_OFFS_USRH 0x17 //[15:0] ZG_OFFS_USR 81 | #define MPU6050_RA_ZG_OFFS_USRL 0x18 82 | #define MPU6050_RA_SMPLRT_DIV 0x19 83 | #define MPU6050_RA_CONFIG 0x1A 84 | #define MPU6050_RA_GYRO_CONFIG 0x1B 85 | #define MPU6050_RA_ACCEL_CONFIG 0x1C 86 | #define MPU6050_RA_FF_THR 0x1D 87 | #define MPU6050_RA_FF_DUR 0x1E 88 | #define MPU6050_RA_MOT_THR 0x1F 89 | #define MPU6050_RA_MOT_DUR 0x20 90 | #define MPU6050_RA_ZRMOT_THR 0x21 91 | #define MPU6050_RA_ZRMOT_DUR 0x22 92 | #define MPU6050_RA_FIFO_EN 0x23 93 | #define MPU6050_RA_I2C_MST_CTRL 0x24 94 | #define MPU6050_RA_I2C_SLV0_ADDR 0x25 95 | #define MPU6050_RA_I2C_SLV0_REG 0x26 96 | #define MPU6050_RA_I2C_SLV0_CTRL 0x27 97 | #define MPU6050_RA_I2C_SLV1_ADDR 0x28 98 | #define MPU6050_RA_I2C_SLV1_REG 0x29 99 | #define MPU6050_RA_I2C_SLV1_CTRL 0x2A 100 | #define MPU6050_RA_I2C_SLV2_ADDR 0x2B 101 | #define MPU6050_RA_I2C_SLV2_REG 0x2C 102 | #define MPU6050_RA_I2C_SLV2_CTRL 0x2D 103 | #define MPU6050_RA_I2C_SLV3_ADDR 0x2E 104 | #define MPU6050_RA_I2C_SLV3_REG 0x2F 105 | #define MPU6050_RA_I2C_SLV3_CTRL 0x30 106 | #define MPU6050_RA_I2C_SLV4_ADDR 0x31 107 | #define MPU6050_RA_I2C_SLV4_REG 0x32 108 | #define MPU6050_RA_I2C_SLV4_DO 0x33 109 | #define MPU6050_RA_I2C_SLV4_CTRL 0x34 110 | #define MPU6050_RA_I2C_SLV4_DI 0x35 111 | #define MPU6050_RA_I2C_MST_STATUS 0x36 112 | #define MPU6050_RA_INT_PIN_CFG 0x37 113 | #define MPU6050_RA_INT_ENABLE 0x38 114 | #define MPU6050_RA_DMP_INT_STATUS 0x39 115 | #define MPU6050_RA_INT_STATUS 0x3A 116 | #define MPU6050_RA_ACCEL_XOUT_H 0x3B 117 | #define MPU6050_RA_ACCEL_XOUT_L 0x3C 118 | #define MPU6050_RA_ACCEL_YOUT_H 0x3D 119 | #define MPU6050_RA_ACCEL_YOUT_L 0x3E 120 | #define MPU6050_RA_ACCEL_ZOUT_H 0x3F 121 | #define MPU6050_RA_ACCEL_ZOUT_L 0x40 122 | #define MPU6050_RA_TEMP_OUT_H 0x41 123 | #define MPU6050_RA_TEMP_OUT_L 0x42 124 | #define MPU6050_RA_GYRO_XOUT_H 0x43 125 | #define MPU6050_RA_GYRO_XOUT_L 0x44 126 | #define MPU6050_RA_GYRO_YOUT_H 0x45 127 | #define MPU6050_RA_GYRO_YOUT_L 0x46 128 | #define MPU6050_RA_GYRO_ZOUT_H 0x47 129 | #define MPU6050_RA_GYRO_ZOUT_L 0x48 130 | #define MPU6050_RA_EXT_SENS_DATA_00 0x49 131 | #define MPU6050_RA_EXT_SENS_DATA_01 0x4A 132 | #define MPU6050_RA_EXT_SENS_DATA_02 0x4B 133 | #define MPU6050_RA_EXT_SENS_DATA_03 0x4C 134 | #define MPU6050_RA_EXT_SENS_DATA_04 0x4D 135 | #define MPU6050_RA_EXT_SENS_DATA_05 0x4E 136 | #define MPU6050_RA_EXT_SENS_DATA_06 0x4F 137 | #define MPU6050_RA_EXT_SENS_DATA_07 0x50 138 | #define MPU6050_RA_EXT_SENS_DATA_08 0x51 139 | #define MPU6050_RA_EXT_SENS_DATA_09 0x52 140 | #define MPU6050_RA_EXT_SENS_DATA_10 0x53 141 | #define MPU6050_RA_EXT_SENS_DATA_11 0x54 142 | #define MPU6050_RA_EXT_SENS_DATA_12 0x55 143 | #define MPU6050_RA_EXT_SENS_DATA_13 0x56 144 | #define MPU6050_RA_EXT_SENS_DATA_14 0x57 145 | #define MPU6050_RA_EXT_SENS_DATA_15 0x58 146 | #define MPU6050_RA_EXT_SENS_DATA_16 0x59 147 | #define MPU6050_RA_EXT_SENS_DATA_17 0x5A 148 | #define MPU6050_RA_EXT_SENS_DATA_18 0x5B 149 | #define MPU6050_RA_EXT_SENS_DATA_19 0x5C 150 | #define MPU6050_RA_EXT_SENS_DATA_20 0x5D 151 | #define MPU6050_RA_EXT_SENS_DATA_21 0x5E 152 | #define MPU6050_RA_EXT_SENS_DATA_22 0x5F 153 | #define MPU6050_RA_EXT_SENS_DATA_23 0x60 154 | #define MPU6050_RA_MOT_DETECT_STATUS 0x61 155 | #define MPU6050_RA_I2C_SLV0_DO 0x63 156 | #define MPU6050_RA_I2C_SLV1_DO 0x64 157 | #define MPU6050_RA_I2C_SLV2_DO 0x65 158 | #define MPU6050_RA_I2C_SLV3_DO 0x66 159 | #define MPU6050_RA_I2C_MST_DELAY_CTRL 0x67 160 | #define MPU6050_RA_SIGNAL_PATH_RESET 0x68 161 | #define MPU6050_RA_MOT_DETECT_CTRL 0x69 162 | #define MPU6050_RA_USER_CTRL 0x6A 163 | #define MPU6050_RA_PWR_MGMT_1 0x6B 164 | #define MPU6050_RA_PWR_MGMT_2 0x6C 165 | #define MPU6050_RA_BANK_SEL 0x6D 166 | #define MPU6050_RA_MEM_START_ADDR 0x6E 167 | #define MPU6050_RA_MEM_R_W 0x6F 168 | #define MPU6050_RA_DMP_CFG_1 0x70 169 | #define MPU6050_RA_DMP_CFG_2 0x71 170 | #define MPU6050_RA_FIFO_COUNTH 0x72 171 | #define MPU6050_RA_FIFO_COUNTL 0x73 172 | #define MPU6050_RA_FIFO_R_W 0x74 173 | #define MPU6050_RA_WHO_AM_I 0x75 174 | 175 | #define MPU6050_SELF_TEST_XA_1_BIT 0x07 176 | #define MPU6050_SELF_TEST_XA_1_LENGTH 0x03 177 | #define MPU6050_SELF_TEST_XA_2_BIT 0x05 178 | #define MPU6050_SELF_TEST_XA_2_LENGTH 0x02 179 | #define MPU6050_SELF_TEST_YA_1_BIT 0x07 180 | #define MPU6050_SELF_TEST_YA_1_LENGTH 0x03 181 | #define MPU6050_SELF_TEST_YA_2_BIT 0x03 182 | #define MPU6050_SELF_TEST_YA_2_LENGTH 0x02 183 | #define MPU6050_SELF_TEST_ZA_1_BIT 0x07 184 | #define MPU6050_SELF_TEST_ZA_1_LENGTH 0x03 185 | #define MPU6050_SELF_TEST_ZA_2_BIT 0x01 186 | #define MPU6050_SELF_TEST_ZA_2_LENGTH 0x02 187 | 188 | #define MPU6050_SELF_TEST_XG_1_BIT 0x04 189 | #define MPU6050_SELF_TEST_XG_1_LENGTH 0x05 190 | #define MPU6050_SELF_TEST_YG_1_BIT 0x04 191 | #define MPU6050_SELF_TEST_YG_1_LENGTH 0x05 192 | #define MPU6050_SELF_TEST_ZG_1_BIT 0x04 193 | #define MPU6050_SELF_TEST_ZG_1_LENGTH 0x05 194 | 195 | #define MPU6050_TC_PWR_MODE_BIT 7 196 | #define MPU6050_TC_OFFSET_BIT 6 197 | #define MPU6050_TC_OFFSET_LENGTH 6 198 | #define MPU6050_TC_OTP_BNK_VLD_BIT 0 199 | 200 | #define MPU6050_VDDIO_LEVEL_VLOGIC 0 201 | #define MPU6050_VDDIO_LEVEL_VDD 1 202 | 203 | #define MPU6050_CFG_EXT_SYNC_SET_BIT 5 204 | #define MPU6050_CFG_EXT_SYNC_SET_LENGTH 3 205 | #define MPU6050_CFG_DLPF_CFG_BIT 2 206 | #define MPU6050_CFG_DLPF_CFG_LENGTH 3 207 | 208 | #define MPU6050_EXT_SYNC_DISABLED 0x0 209 | #define MPU6050_EXT_SYNC_TEMP_OUT_L 0x1 210 | #define MPU6050_EXT_SYNC_GYRO_XOUT_L 0x2 211 | #define MPU6050_EXT_SYNC_GYRO_YOUT_L 0x3 212 | #define MPU6050_EXT_SYNC_GYRO_ZOUT_L 0x4 213 | #define MPU6050_EXT_SYNC_ACCEL_XOUT_L 0x5 214 | #define MPU6050_EXT_SYNC_ACCEL_YOUT_L 0x6 215 | #define MPU6050_EXT_SYNC_ACCEL_ZOUT_L 0x7 216 | 217 | #define MPU6050_DLPF_BW_256 0x00 218 | #define MPU6050_DLPF_BW_188 0x01 219 | #define MPU6050_DLPF_BW_98 0x02 220 | #define MPU6050_DLPF_BW_42 0x03 221 | #define MPU6050_DLPF_BW_20 0x04 222 | #define MPU6050_DLPF_BW_10 0x05 223 | #define MPU6050_DLPF_BW_5 0x06 224 | 225 | #define MPU6050_GCONFIG_FS_SEL_BIT 4 226 | #define MPU6050_GCONFIG_FS_SEL_LENGTH 2 227 | 228 | #define MPU6050_GYRO_FS_250 0x00 229 | #define MPU6050_GYRO_FS_500 0x01 230 | #define MPU6050_GYRO_FS_1000 0x02 231 | #define MPU6050_GYRO_FS_2000 0x03 232 | 233 | #define MPU6050_ACONFIG_XA_ST_BIT 7 234 | #define MPU6050_ACONFIG_YA_ST_BIT 6 235 | #define MPU6050_ACONFIG_ZA_ST_BIT 5 236 | #define MPU6050_ACONFIG_AFS_SEL_BIT 4 237 | #define MPU6050_ACONFIG_AFS_SEL_LENGTH 2 238 | #define MPU6050_ACONFIG_ACCEL_HPF_BIT 2 239 | #define MPU6050_ACONFIG_ACCEL_HPF_LENGTH 3 240 | 241 | #define MPU6050_ACCEL_FS_2 0x00 242 | #define MPU6050_ACCEL_FS_4 0x01 243 | #define MPU6050_ACCEL_FS_8 0x02 244 | #define MPU6050_ACCEL_FS_16 0x03 245 | 246 | #define MPU6050_DHPF_RESET 0x00 247 | #define MPU6050_DHPF_5 0x01 248 | #define MPU6050_DHPF_2P5 0x02 249 | #define MPU6050_DHPF_1P25 0x03 250 | #define MPU6050_DHPF_0P63 0x04 251 | #define MPU6050_DHPF_HOLD 0x07 252 | 253 | #define MPU6050_TEMP_FIFO_EN_BIT 7 254 | #define MPU6050_XG_FIFO_EN_BIT 6 255 | #define MPU6050_YG_FIFO_EN_BIT 5 256 | #define MPU6050_ZG_FIFO_EN_BIT 4 257 | #define MPU6050_ACCEL_FIFO_EN_BIT 3 258 | #define MPU6050_SLV2_FIFO_EN_BIT 2 259 | #define MPU6050_SLV1_FIFO_EN_BIT 1 260 | #define MPU6050_SLV0_FIFO_EN_BIT 0 261 | 262 | #define MPU6050_MULT_MST_EN_BIT 7 263 | #define MPU6050_WAIT_FOR_ES_BIT 6 264 | #define MPU6050_SLV_3_FIFO_EN_BIT 5 265 | #define MPU6050_I2C_MST_P_NSR_BIT 4 266 | #define MPU6050_I2C_MST_CLK_BIT 3 267 | #define MPU6050_I2C_MST_CLK_LENGTH 4 268 | 269 | #define MPU6050_CLOCK_DIV_348 0x0 270 | #define MPU6050_CLOCK_DIV_333 0x1 271 | #define MPU6050_CLOCK_DIV_320 0x2 272 | #define MPU6050_CLOCK_DIV_308 0x3 273 | #define MPU6050_CLOCK_DIV_296 0x4 274 | #define MPU6050_CLOCK_DIV_286 0x5 275 | #define MPU6050_CLOCK_DIV_276 0x6 276 | #define MPU6050_CLOCK_DIV_267 0x7 277 | #define MPU6050_CLOCK_DIV_258 0x8 278 | #define MPU6050_CLOCK_DIV_500 0x9 279 | #define MPU6050_CLOCK_DIV_471 0xA 280 | #define MPU6050_CLOCK_DIV_444 0xB 281 | #define MPU6050_CLOCK_DIV_421 0xC 282 | #define MPU6050_CLOCK_DIV_400 0xD 283 | #define MPU6050_CLOCK_DIV_381 0xE 284 | #define MPU6050_CLOCK_DIV_364 0xF 285 | 286 | #define MPU6050_I2C_SLV_RW_BIT 7 287 | #define MPU6050_I2C_SLV_ADDR_BIT 6 288 | #define MPU6050_I2C_SLV_ADDR_LENGTH 7 289 | #define MPU6050_I2C_SLV_EN_BIT 7 290 | #define MPU6050_I2C_SLV_BYTE_SW_BIT 6 291 | #define MPU6050_I2C_SLV_REG_DIS_BIT 5 292 | #define MPU6050_I2C_SLV_GRP_BIT 4 293 | #define MPU6050_I2C_SLV_LEN_BIT 3 294 | #define MPU6050_I2C_SLV_LEN_LENGTH 4 295 | 296 | #define MPU6050_I2C_SLV4_RW_BIT 7 297 | #define MPU6050_I2C_SLV4_ADDR_BIT 6 298 | #define MPU6050_I2C_SLV4_ADDR_LENGTH 7 299 | #define MPU6050_I2C_SLV4_EN_BIT 7 300 | #define MPU6050_I2C_SLV4_INT_EN_BIT 6 301 | #define MPU6050_I2C_SLV4_REG_DIS_BIT 5 302 | #define MPU6050_I2C_SLV4_MST_DLY_BIT 4 303 | #define MPU6050_I2C_SLV4_MST_DLY_LENGTH 5 304 | 305 | #define MPU6050_MST_PASS_THROUGH_BIT 7 306 | #define MPU6050_MST_I2C_SLV4_DONE_BIT 6 307 | #define MPU6050_MST_I2C_LOST_ARB_BIT 5 308 | #define MPU6050_MST_I2C_SLV4_NACK_BIT 4 309 | #define MPU6050_MST_I2C_SLV3_NACK_BIT 3 310 | #define MPU6050_MST_I2C_SLV2_NACK_BIT 2 311 | #define MPU6050_MST_I2C_SLV1_NACK_BIT 1 312 | #define MPU6050_MST_I2C_SLV0_NACK_BIT 0 313 | 314 | #define MPU6050_INTCFG_INT_LEVEL_BIT 7 315 | #define MPU6050_INTCFG_INT_OPEN_BIT 6 316 | #define MPU6050_INTCFG_LATCH_INT_EN_BIT 5 317 | #define MPU6050_INTCFG_INT_RD_CLEAR_BIT 4 318 | #define MPU6050_INTCFG_FSYNC_INT_LEVEL_BIT 3 319 | #define MPU6050_INTCFG_FSYNC_INT_EN_BIT 2 320 | #define MPU6050_INTCFG_I2C_BYPASS_EN_BIT 1 321 | #define MPU6050_INTCFG_CLKOUT_EN_BIT 0 322 | 323 | #define MPU6050_INTMODE_ACTIVEHIGH 0x00 324 | #define MPU6050_INTMODE_ACTIVELOW 0x01 325 | 326 | #define MPU6050_INTDRV_PUSHPULL 0x00 327 | #define MPU6050_INTDRV_OPENDRAIN 0x01 328 | 329 | #define MPU6050_INTLATCH_50USPULSE 0x00 330 | #define MPU6050_INTLATCH_WAITCLEAR 0x01 331 | 332 | #define MPU6050_INTCLEAR_STATUSREAD 0x00 333 | #define MPU6050_INTCLEAR_ANYREAD 0x01 334 | 335 | #define MPU6050_INTERRUPT_FF_BIT 7 336 | #define MPU6050_INTERRUPT_MOT_BIT 6 337 | #define MPU6050_INTERRUPT_ZMOT_BIT 5 338 | #define MPU6050_INTERRUPT_FIFO_OFLOW_BIT 4 339 | #define MPU6050_INTERRUPT_I2C_MST_INT_BIT 3 340 | #define MPU6050_INTERRUPT_PLL_RDY_INT_BIT 2 341 | #define MPU6050_INTERRUPT_DMP_INT_BIT 1 342 | #define MPU6050_INTERRUPT_DATA_RDY_BIT 0 343 | 344 | // TODO: figure out what these actually do 345 | // UMPL source code is not very obivous 346 | #define MPU6050_DMPINT_5_BIT 5 347 | #define MPU6050_DMPINT_4_BIT 4 348 | #define MPU6050_DMPINT_3_BIT 3 349 | #define MPU6050_DMPINT_2_BIT 2 350 | #define MPU6050_DMPINT_1_BIT 1 351 | #define MPU6050_DMPINT_0_BIT 0 352 | 353 | #define MPU6050_MOTION_MOT_XNEG_BIT 7 354 | #define MPU6050_MOTION_MOT_XPOS_BIT 6 355 | #define MPU6050_MOTION_MOT_YNEG_BIT 5 356 | #define MPU6050_MOTION_MOT_YPOS_BIT 4 357 | #define MPU6050_MOTION_MOT_ZNEG_BIT 3 358 | #define MPU6050_MOTION_MOT_ZPOS_BIT 2 359 | #define MPU6050_MOTION_MOT_ZRMOT_BIT 0 360 | 361 | #define MPU6050_DELAYCTRL_DELAY_ES_SHADOW_BIT 7 362 | #define MPU6050_DELAYCTRL_I2C_SLV4_DLY_EN_BIT 4 363 | #define MPU6050_DELAYCTRL_I2C_SLV3_DLY_EN_BIT 3 364 | #define MPU6050_DELAYCTRL_I2C_SLV2_DLY_EN_BIT 2 365 | #define MPU6050_DELAYCTRL_I2C_SLV1_DLY_EN_BIT 1 366 | #define MPU6050_DELAYCTRL_I2C_SLV0_DLY_EN_BIT 0 367 | 368 | #define MPU6050_PATHRESET_GYRO_RESET_BIT 2 369 | #define MPU6050_PATHRESET_ACCEL_RESET_BIT 1 370 | #define MPU6050_PATHRESET_TEMP_RESET_BIT 0 371 | 372 | #define MPU6050_DETECT_ACCEL_ON_DELAY_BIT 5 373 | #define MPU6050_DETECT_ACCEL_ON_DELAY_LENGTH 2 374 | #define MPU6050_DETECT_FF_COUNT_BIT 3 375 | #define MPU6050_DETECT_FF_COUNT_LENGTH 2 376 | #define MPU6050_DETECT_MOT_COUNT_BIT 1 377 | #define MPU6050_DETECT_MOT_COUNT_LENGTH 2 378 | 379 | #define MPU6050_DETECT_DECREMENT_RESET 0x0 380 | #define MPU6050_DETECT_DECREMENT_1 0x1 381 | #define MPU6050_DETECT_DECREMENT_2 0x2 382 | #define MPU6050_DETECT_DECREMENT_4 0x3 383 | 384 | #define MPU6050_USERCTRL_DMP_EN_BIT 7 385 | #define MPU6050_USERCTRL_FIFO_EN_BIT 6 386 | #define MPU6050_USERCTRL_I2C_MST_EN_BIT 5 387 | #define MPU6050_USERCTRL_I2C_IF_DIS_BIT 4 388 | #define MPU6050_USERCTRL_DMP_RESET_BIT 3 389 | #define MPU6050_USERCTRL_FIFO_RESET_BIT 2 390 | #define MPU6050_USERCTRL_I2C_MST_RESET_BIT 1 391 | #define MPU6050_USERCTRL_SIG_COND_RESET_BIT 0 392 | 393 | #define MPU6050_PWR1_DEVICE_RESET_BIT 7 394 | #define MPU6050_PWR1_SLEEP_BIT 6 395 | #define MPU6050_PWR1_CYCLE_BIT 5 396 | #define MPU6050_PWR1_TEMP_DIS_BIT 3 397 | #define MPU6050_PWR1_CLKSEL_BIT 2 398 | #define MPU6050_PWR1_CLKSEL_LENGTH 3 399 | 400 | #define MPU6050_CLOCK_INTERNAL 0x00 401 | #define MPU6050_CLOCK_PLL_XGYRO 0x01 402 | #define MPU6050_CLOCK_PLL_YGYRO 0x02 403 | #define MPU6050_CLOCK_PLL_ZGYRO 0x03 404 | #define MPU6050_CLOCK_PLL_EXT32K 0x04 405 | #define MPU6050_CLOCK_PLL_EXT19M 0x05 406 | #define MPU6050_CLOCK_KEEP_RESET 0x07 407 | 408 | #define MPU6050_PWR2_LP_WAKE_CTRL_BIT 7 409 | #define MPU6050_PWR2_LP_WAKE_CTRL_LENGTH 2 410 | #define MPU6050_PWR2_STBY_XA_BIT 5 411 | #define MPU6050_PWR2_STBY_YA_BIT 4 412 | #define MPU6050_PWR2_STBY_ZA_BIT 3 413 | #define MPU6050_PWR2_STBY_XG_BIT 2 414 | #define MPU6050_PWR2_STBY_YG_BIT 1 415 | #define MPU6050_PWR2_STBY_ZG_BIT 0 416 | 417 | #define MPU6050_WAKE_FREQ_1P25 0x0 418 | #define MPU6050_WAKE_FREQ_2P5 0x1 419 | #define MPU6050_WAKE_FREQ_5 0x2 420 | #define MPU6050_WAKE_FREQ_10 0x3 421 | 422 | #define MPU6050_BANKSEL_PRFTCH_EN_BIT 6 423 | #define MPU6050_BANKSEL_CFG_USER_BANK_BIT 5 424 | #define MPU6050_BANKSEL_MEM_SEL_BIT 4 425 | #define MPU6050_BANKSEL_MEM_SEL_LENGTH 5 426 | 427 | #define MPU6050_WHO_AM_I_BIT 6 428 | #define MPU6050_WHO_AM_I_LENGTH 6 429 | 430 | #define MPU6050_DMP_MEMORY_BANKS 8 431 | #define MPU6050_DMP_MEMORY_BANK_SIZE 256 432 | #define MPU6050_DMP_MEMORY_CHUNK_SIZE 16 433 | 434 | // note: DMP code memory blocks defined at end of header file 435 | 436 | class MPU6050 { 437 | public: 438 | MPU6050(uint8_t address=MPU6050_DEFAULT_ADDRESS); 439 | 440 | void initialize(); 441 | bool testConnection(); 442 | 443 | // AUX_VDDIO register 444 | uint8_t getAuxVDDIOLevel(); 445 | void setAuxVDDIOLevel(uint8_t level); 446 | 447 | // SMPLRT_DIV register 448 | uint8_t getRate(); 449 | void setRate(uint8_t rate); 450 | 451 | // CONFIG register 452 | uint8_t getExternalFrameSync(); 453 | void setExternalFrameSync(uint8_t sync); 454 | uint8_t getDLPFMode(); 455 | void setDLPFMode(uint8_t bandwidth); 456 | 457 | // GYRO_CONFIG register 458 | uint8_t getFullScaleGyroRange(); 459 | void setFullScaleGyroRange(uint8_t range); 460 | 461 | // SELF_TEST registers 462 | uint8_t getAccelXSelfTestFactoryTrim(); 463 | uint8_t getAccelYSelfTestFactoryTrim(); 464 | uint8_t getAccelZSelfTestFactoryTrim(); 465 | 466 | uint8_t getGyroXSelfTestFactoryTrim(); 467 | uint8_t getGyroYSelfTestFactoryTrim(); 468 | uint8_t getGyroZSelfTestFactoryTrim(); 469 | 470 | // ACCEL_CONFIG register 471 | bool getAccelXSelfTest(); 472 | void setAccelXSelfTest(bool enabled); 473 | bool getAccelYSelfTest(); 474 | void setAccelYSelfTest(bool enabled); 475 | bool getAccelZSelfTest(); 476 | void setAccelZSelfTest(bool enabled); 477 | uint8_t getFullScaleAccelRange(); 478 | void setFullScaleAccelRange(uint8_t range); 479 | uint8_t getDHPFMode(); 480 | void setDHPFMode(uint8_t mode); 481 | 482 | // FF_THR register 483 | uint8_t getFreefallDetectionThreshold(); 484 | void setFreefallDetectionThreshold(uint8_t threshold); 485 | 486 | // FF_DUR register 487 | uint8_t getFreefallDetectionDuration(); 488 | void setFreefallDetectionDuration(uint8_t duration); 489 | 490 | // MOT_THR register 491 | uint8_t getMotionDetectionThreshold(); 492 | void setMotionDetectionThreshold(uint8_t threshold); 493 | 494 | // MOT_DUR register 495 | uint8_t getMotionDetectionDuration(); 496 | void setMotionDetectionDuration(uint8_t duration); 497 | 498 | // ZRMOT_THR register 499 | uint8_t getZeroMotionDetectionThreshold(); 500 | void setZeroMotionDetectionThreshold(uint8_t threshold); 501 | 502 | // ZRMOT_DUR register 503 | uint8_t getZeroMotionDetectionDuration(); 504 | void setZeroMotionDetectionDuration(uint8_t duration); 505 | 506 | // FIFO_EN register 507 | bool getTempFIFOEnabled(); 508 | void setTempFIFOEnabled(bool enabled); 509 | bool getXGyroFIFOEnabled(); 510 | void setXGyroFIFOEnabled(bool enabled); 511 | bool getYGyroFIFOEnabled(); 512 | void setYGyroFIFOEnabled(bool enabled); 513 | bool getZGyroFIFOEnabled(); 514 | void setZGyroFIFOEnabled(bool enabled); 515 | bool getAccelFIFOEnabled(); 516 | void setAccelFIFOEnabled(bool enabled); 517 | bool getSlave2FIFOEnabled(); 518 | void setSlave2FIFOEnabled(bool enabled); 519 | bool getSlave1FIFOEnabled(); 520 | void setSlave1FIFOEnabled(bool enabled); 521 | bool getSlave0FIFOEnabled(); 522 | void setSlave0FIFOEnabled(bool enabled); 523 | 524 | // I2C_MST_CTRL register 525 | bool getMultiMasterEnabled(); 526 | void setMultiMasterEnabled(bool enabled); 527 | bool getWaitForExternalSensorEnabled(); 528 | void setWaitForExternalSensorEnabled(bool enabled); 529 | bool getSlave3FIFOEnabled(); 530 | void setSlave3FIFOEnabled(bool enabled); 531 | bool getSlaveReadWriteTransitionEnabled(); 532 | void setSlaveReadWriteTransitionEnabled(bool enabled); 533 | uint8_t getMasterClockSpeed(); 534 | void setMasterClockSpeed(uint8_t speed); 535 | 536 | // I2C_SLV* registers (Slave 0-3) 537 | uint8_t getSlaveAddress(uint8_t num); 538 | void setSlaveAddress(uint8_t num, uint8_t address); 539 | uint8_t getSlaveRegister(uint8_t num); 540 | void setSlaveRegister(uint8_t num, uint8_t reg); 541 | bool getSlaveEnabled(uint8_t num); 542 | void setSlaveEnabled(uint8_t num, bool enabled); 543 | bool getSlaveWordByteSwap(uint8_t num); 544 | void setSlaveWordByteSwap(uint8_t num, bool enabled); 545 | bool getSlaveWriteMode(uint8_t num); 546 | void setSlaveWriteMode(uint8_t num, bool mode); 547 | bool getSlaveWordGroupOffset(uint8_t num); 548 | void setSlaveWordGroupOffset(uint8_t num, bool enabled); 549 | uint8_t getSlaveDataLength(uint8_t num); 550 | void setSlaveDataLength(uint8_t num, uint8_t length); 551 | 552 | // I2C_SLV* registers (Slave 4) 553 | uint8_t getSlave4Address(); 554 | void setSlave4Address(uint8_t address); 555 | uint8_t getSlave4Register(); 556 | void setSlave4Register(uint8_t reg); 557 | void setSlave4OutputByte(uint8_t data); 558 | bool getSlave4Enabled(); 559 | void setSlave4Enabled(bool enabled); 560 | bool getSlave4InterruptEnabled(); 561 | void setSlave4InterruptEnabled(bool enabled); 562 | bool getSlave4WriteMode(); 563 | void setSlave4WriteMode(bool mode); 564 | uint8_t getSlave4MasterDelay(); 565 | void setSlave4MasterDelay(uint8_t delay); 566 | uint8_t getSlate4InputByte(); 567 | 568 | // I2C_MST_STATUS register 569 | bool getPassthroughStatus(); 570 | bool getSlave4IsDone(); 571 | bool getLostArbitration(); 572 | bool getSlave4Nack(); 573 | bool getSlave3Nack(); 574 | bool getSlave2Nack(); 575 | bool getSlave1Nack(); 576 | bool getSlave0Nack(); 577 | 578 | // INT_PIN_CFG register 579 | bool getInterruptMode(); 580 | void setInterruptMode(bool mode); 581 | bool getInterruptDrive(); 582 | void setInterruptDrive(bool drive); 583 | bool getInterruptLatch(); 584 | void setInterruptLatch(bool latch); 585 | bool getInterruptLatchClear(); 586 | void setInterruptLatchClear(bool clear); 587 | bool getFSyncInterruptLevel(); 588 | void setFSyncInterruptLevel(bool level); 589 | bool getFSyncInterruptEnabled(); 590 | void setFSyncInterruptEnabled(bool enabled); 591 | bool getI2CBypassEnabled(); 592 | void setI2CBypassEnabled(bool enabled); 593 | bool getClockOutputEnabled(); 594 | void setClockOutputEnabled(bool enabled); 595 | 596 | // INT_ENABLE register 597 | uint8_t getIntEnabled(); 598 | void setIntEnabled(uint8_t enabled); 599 | bool getIntFreefallEnabled(); 600 | void setIntFreefallEnabled(bool enabled); 601 | bool getIntMotionEnabled(); 602 | void setIntMotionEnabled(bool enabled); 603 | bool getIntZeroMotionEnabled(); 604 | void setIntZeroMotionEnabled(bool enabled); 605 | bool getIntFIFOBufferOverflowEnabled(); 606 | void setIntFIFOBufferOverflowEnabled(bool enabled); 607 | bool getIntI2CMasterEnabled(); 608 | void setIntI2CMasterEnabled(bool enabled); 609 | bool getIntDataReadyEnabled(); 610 | void setIntDataReadyEnabled(bool enabled); 611 | 612 | // INT_STATUS register 613 | uint8_t getIntStatus(); 614 | bool getIntFreefallStatus(); 615 | bool getIntMotionStatus(); 616 | bool getIntZeroMotionStatus(); 617 | bool getIntFIFOBufferOverflowStatus(); 618 | bool getIntI2CMasterStatus(); 619 | bool getIntDataReadyStatus(); 620 | 621 | // ACCEL_*OUT_* registers 622 | void getMotion9(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz, int16_t* mx, int16_t* my, int16_t* mz); 623 | void getMotion6(int16_t* ax, int16_t* ay, int16_t* az, int16_t* gx, int16_t* gy, int16_t* gz); 624 | void getAcceleration(int16_t* x, int16_t* y, int16_t* z); 625 | int16_t getAccelerationX(); 626 | int16_t getAccelerationY(); 627 | int16_t getAccelerationZ(); 628 | 629 | // TEMP_OUT_* registers 630 | int16_t getTemperature(); 631 | 632 | // GYRO_*OUT_* registers 633 | void getRotation(int16_t* x, int16_t* y, int16_t* z); 634 | int16_t getRotationX(); 635 | int16_t getRotationY(); 636 | int16_t getRotationZ(); 637 | 638 | // EXT_SENS_DATA_* registers 639 | uint8_t getExternalSensorByte(int position); 640 | uint16_t getExternalSensorWord(int position); 641 | uint32_t getExternalSensorDWord(int position); 642 | 643 | // MOT_DETECT_STATUS register 644 | uint8_t getMotionStatus(); 645 | bool getXNegMotionDetected(); 646 | bool getXPosMotionDetected(); 647 | bool getYNegMotionDetected(); 648 | bool getYPosMotionDetected(); 649 | bool getZNegMotionDetected(); 650 | bool getZPosMotionDetected(); 651 | bool getZeroMotionDetected(); 652 | 653 | // I2C_SLV*_DO register 654 | void setSlaveOutputByte(uint8_t num, uint8_t data); 655 | 656 | // I2C_MST_DELAY_CTRL register 657 | bool getExternalShadowDelayEnabled(); 658 | void setExternalShadowDelayEnabled(bool enabled); 659 | bool getSlaveDelayEnabled(uint8_t num); 660 | void setSlaveDelayEnabled(uint8_t num, bool enabled); 661 | 662 | // SIGNAL_PATH_RESET register 663 | void resetGyroscopePath(); 664 | void resetAccelerometerPath(); 665 | void resetTemperaturePath(); 666 | 667 | // MOT_DETECT_CTRL register 668 | uint8_t getAccelerometerPowerOnDelay(); 669 | void setAccelerometerPowerOnDelay(uint8_t delay); 670 | uint8_t getFreefallDetectionCounterDecrement(); 671 | void setFreefallDetectionCounterDecrement(uint8_t decrement); 672 | uint8_t getMotionDetectionCounterDecrement(); 673 | void setMotionDetectionCounterDecrement(uint8_t decrement); 674 | 675 | // USER_CTRL register 676 | bool getFIFOEnabled(); 677 | void setFIFOEnabled(bool enabled); 678 | bool getI2CMasterModeEnabled(); 679 | void setI2CMasterModeEnabled(bool enabled); 680 | void switchSPIEnabled(bool enabled); 681 | void resetFIFO(); 682 | void resetI2CMaster(); 683 | void resetSensors(); 684 | 685 | // PWR_MGMT_1 register 686 | void reset(); 687 | bool getSleepEnabled(); 688 | void setSleepEnabled(bool enabled); 689 | bool getWakeCycleEnabled(); 690 | void setWakeCycleEnabled(bool enabled); 691 | bool getTempSensorEnabled(); 692 | void setTempSensorEnabled(bool enabled); 693 | uint8_t getClockSource(); 694 | void setClockSource(uint8_t source); 695 | 696 | // PWR_MGMT_2 register 697 | uint8_t getWakeFrequency(); 698 | void setWakeFrequency(uint8_t frequency); 699 | bool getStandbyXAccelEnabled(); 700 | void setStandbyXAccelEnabled(bool enabled); 701 | bool getStandbyYAccelEnabled(); 702 | void setStandbyYAccelEnabled(bool enabled); 703 | bool getStandbyZAccelEnabled(); 704 | void setStandbyZAccelEnabled(bool enabled); 705 | bool getStandbyXGyroEnabled(); 706 | void setStandbyXGyroEnabled(bool enabled); 707 | bool getStandbyYGyroEnabled(); 708 | void setStandbyYGyroEnabled(bool enabled); 709 | bool getStandbyZGyroEnabled(); 710 | void setStandbyZGyroEnabled(bool enabled); 711 | 712 | // FIFO_COUNT_* registers 713 | uint16_t getFIFOCount(); 714 | 715 | // FIFO_R_W register 716 | uint8_t getFIFOByte(); 717 | int8_t GetCurrentFIFOPacket(uint8_t *data, uint8_t length); 718 | void setFIFOByte(uint8_t data); 719 | void getFIFOBytes(uint8_t *data, uint8_t length); 720 | 721 | // WHO_AM_I register 722 | uint8_t getDeviceID(); 723 | void setDeviceID(uint8_t id); 724 | 725 | // ======== UNDOCUMENTED/DMP REGISTERS/METHODS ======== 726 | 727 | // XG_OFFS_TC register 728 | uint8_t getOTPBankValid(); 729 | void setOTPBankValid(bool enabled); 730 | int8_t getXGyroOffsetTC(); 731 | void setXGyroOffsetTC(int8_t offset); 732 | 733 | // YG_OFFS_TC register 734 | int8_t getYGyroOffsetTC(); 735 | void setYGyroOffsetTC(int8_t offset); 736 | 737 | // ZG_OFFS_TC register 738 | int8_t getZGyroOffsetTC(); 739 | void setZGyroOffsetTC(int8_t offset); 740 | 741 | // X_FINE_GAIN register 742 | int8_t getXFineGain(); 743 | void setXFineGain(int8_t gain); 744 | 745 | // Y_FINE_GAIN register 746 | int8_t getYFineGain(); 747 | void setYFineGain(int8_t gain); 748 | 749 | // Z_FINE_GAIN register 750 | int8_t getZFineGain(); 751 | void setZFineGain(int8_t gain); 752 | 753 | // XA_OFFS_* registers 754 | int16_t getXAccelOffset(); 755 | void setXAccelOffset(int16_t offset); 756 | 757 | // YA_OFFS_* register 758 | int16_t getYAccelOffset(); 759 | void setYAccelOffset(int16_t offset); 760 | 761 | // ZA_OFFS_* register 762 | int16_t getZAccelOffset(); 763 | void setZAccelOffset(int16_t offset); 764 | 765 | // XG_OFFS_USR* registers 766 | int16_t getXGyroOffset(); 767 | void setXGyroOffset(int16_t offset); 768 | 769 | // YG_OFFS_USR* register 770 | int16_t getYGyroOffset(); 771 | void setYGyroOffset(int16_t offset); 772 | 773 | // ZG_OFFS_USR* register 774 | int16_t getZGyroOffset(); 775 | void setZGyroOffset(int16_t offset); 776 | 777 | // INT_ENABLE register (DMP functions) 778 | bool getIntPLLReadyEnabled(); 779 | void setIntPLLReadyEnabled(bool enabled); 780 | bool getIntDMPEnabled(); 781 | void setIntDMPEnabled(bool enabled); 782 | 783 | // DMP_INT_STATUS 784 | bool getDMPInt5Status(); 785 | bool getDMPInt4Status(); 786 | bool getDMPInt3Status(); 787 | bool getDMPInt2Status(); 788 | bool getDMPInt1Status(); 789 | bool getDMPInt0Status(); 790 | 791 | // INT_STATUS register (DMP functions) 792 | bool getIntPLLReadyStatus(); 793 | bool getIntDMPStatus(); 794 | 795 | // USER_CTRL register (DMP functions) 796 | bool getDMPEnabled(); 797 | void setDMPEnabled(bool enabled); 798 | void resetDMP(); 799 | 800 | // BANK_SEL register 801 | void setMemoryBank(uint8_t bank, bool prefetchEnabled=false, bool userBank=false); 802 | 803 | // MEM_START_ADDR register 804 | void setMemoryStartAddress(uint8_t address); 805 | 806 | // MEM_R_W register 807 | uint8_t readMemoryByte(); 808 | void writeMemoryByte(uint8_t data); 809 | void readMemoryBlock(uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0); 810 | bool writeMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0, bool verify=true, bool useProgMem=false); 811 | bool writeProgMemoryBlock(const uint8_t *data, uint16_t dataSize, uint8_t bank=0, uint8_t address=0, bool verify=true); 812 | 813 | bool writeDMPConfigurationSet(const uint8_t *data, uint16_t dataSize, bool useProgMem=false); 814 | bool writeProgDMPConfigurationSet(const uint8_t *data, uint16_t dataSize); 815 | 816 | // DMP_CFG_1 register 817 | uint8_t getDMPConfig1(); 818 | void setDMPConfig1(uint8_t config); 819 | 820 | // DMP_CFG_2 register 821 | uint8_t getDMPConfig2(); 822 | void setDMPConfig2(uint8_t config); 823 | 824 | // Calibration Routines 825 | void CalibrateGyro(uint8_t Loops = 15); // Fine tune after setting offsets with less Loops. 826 | void CalibrateAccel(uint8_t Loops = 15);// Fine tune after setting offsets with less Loops. 827 | void PID(uint8_t ReadAddress, float kP,float kI, uint8_t Loops); // Does the math 828 | void PrintActiveOffsets(); // See the results of the Calibration 829 | 830 | 831 | 832 | // special methods for MotionApps 2.0 implementation 833 | #ifdef MPU6050_INCLUDE_DMP_MOTIONAPPS20 834 | 835 | uint8_t dmpInitialize(); 836 | bool dmpPacketAvailable(); 837 | 838 | uint8_t dmpSetFIFORate(uint8_t fifoRate); 839 | uint8_t dmpGetFIFORate(); 840 | uint8_t dmpGetSampleStepSizeMS(); 841 | uint8_t dmpGetSampleFrequency(); 842 | int32_t dmpDecodeTemperature(int8_t tempReg); 843 | 844 | // Register callbacks after a packet of FIFO data is processed 845 | //uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority); 846 | //uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func); 847 | uint8_t dmpRunFIFORateProcesses(); 848 | 849 | // Setup FIFO for various output 850 | uint8_t dmpSendQuaternion(uint_fast16_t accuracy); 851 | uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy); 852 | uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy); 853 | uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy); 854 | uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy); 855 | uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy); 856 | uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy); 857 | uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy); 858 | uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy); 859 | uint8_t dmpSendPacketNumber(uint_fast16_t accuracy); 860 | uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy); 861 | uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy); 862 | 863 | // Get Fixed Point data from FIFO 864 | uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0); 865 | uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0); 866 | uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0); 867 | uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0); 868 | uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0); 869 | uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0); 870 | uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0); 871 | uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0); 872 | uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0); 873 | uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0); 874 | uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0); 875 | uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0); 876 | uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0); 877 | uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0); 878 | uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0); 879 | uint8_t dmpSetLinearAccelFilterCoefficient(float coef); 880 | uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0); 881 | uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0); 882 | uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0); 883 | uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity); 884 | uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0); 885 | uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0); 886 | uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0); 887 | uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q); 888 | uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0); 889 | uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0); 890 | uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0); 891 | uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0); 892 | uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0); 893 | uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0); 894 | uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0); 895 | uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0); 896 | uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0); 897 | uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0); 898 | uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0); 899 | uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q); 900 | uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0); 901 | uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0); 902 | uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0); 903 | uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0); 904 | uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0); 905 | uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0); 906 | uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0); 907 | uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0); 908 | 909 | uint8_t dmpGetEuler(float *data, Quaternion *q); 910 | uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity); 911 | 912 | // Get Floating Point data from FIFO 913 | uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0); 914 | uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0); 915 | 916 | uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData); 917 | uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL); 918 | 919 | uint8_t dmpSetFIFOProcessedCallback(void (*func) (void)); 920 | 921 | uint8_t dmpInitFIFOParam(); 922 | uint8_t dmpCloseFIFO(); 923 | uint8_t dmpSetGyroDataSource(uint8_t source); 924 | uint8_t dmpDecodeQuantizedAccel(); 925 | uint32_t dmpGetGyroSumOfSquare(); 926 | uint32_t dmpGetAccelSumOfSquare(); 927 | void dmpOverrideQuaternion(long *q); 928 | uint16_t dmpGetFIFOPacketSize(); 929 | uint8_t dmpGetCurrentFIFOPacket(uint8_t *data); // overflow proof 930 | #endif 931 | 932 | // special methods for MotionApps 4.1 implementation 933 | #ifdef MPU6050_INCLUDE_DMP_MOTIONAPPS41 934 | 935 | uint8_t dmpInitialize(); 936 | bool dmpPacketAvailable(); 937 | 938 | uint8_t dmpSetFIFORate(uint8_t fifoRate); 939 | uint8_t dmpGetFIFORate(); 940 | uint8_t dmpGetSampleStepSizeMS(); 941 | uint8_t dmpGetSampleFrequency(); 942 | int32_t dmpDecodeTemperature(int8_t tempReg); 943 | 944 | // Register callbacks after a packet of FIFO data is processed 945 | //uint8_t dmpRegisterFIFORateProcess(inv_obj_func func, int16_t priority); 946 | //uint8_t dmpUnregisterFIFORateProcess(inv_obj_func func); 947 | uint8_t dmpRunFIFORateProcesses(); 948 | 949 | // Setup FIFO for various output 950 | uint8_t dmpSendQuaternion(uint_fast16_t accuracy); 951 | uint8_t dmpSendGyro(uint_fast16_t elements, uint_fast16_t accuracy); 952 | uint8_t dmpSendAccel(uint_fast16_t elements, uint_fast16_t accuracy); 953 | uint8_t dmpSendLinearAccel(uint_fast16_t elements, uint_fast16_t accuracy); 954 | uint8_t dmpSendLinearAccelInWorld(uint_fast16_t elements, uint_fast16_t accuracy); 955 | uint8_t dmpSendControlData(uint_fast16_t elements, uint_fast16_t accuracy); 956 | uint8_t dmpSendSensorData(uint_fast16_t elements, uint_fast16_t accuracy); 957 | uint8_t dmpSendExternalSensorData(uint_fast16_t elements, uint_fast16_t accuracy); 958 | uint8_t dmpSendGravity(uint_fast16_t elements, uint_fast16_t accuracy); 959 | uint8_t dmpSendPacketNumber(uint_fast16_t accuracy); 960 | uint8_t dmpSendQuantizedAccel(uint_fast16_t elements, uint_fast16_t accuracy); 961 | uint8_t dmpSendEIS(uint_fast16_t elements, uint_fast16_t accuracy); 962 | 963 | // Get Fixed Point data from FIFO 964 | uint8_t dmpGetAccel(int32_t *data, const uint8_t* packet=0); 965 | uint8_t dmpGetAccel(int16_t *data, const uint8_t* packet=0); 966 | uint8_t dmpGetAccel(VectorInt16 *v, const uint8_t* packet=0); 967 | uint8_t dmpGetQuaternion(int32_t *data, const uint8_t* packet=0); 968 | uint8_t dmpGetQuaternion(int16_t *data, const uint8_t* packet=0); 969 | uint8_t dmpGetQuaternion(Quaternion *q, const uint8_t* packet=0); 970 | uint8_t dmpGet6AxisQuaternion(int32_t *data, const uint8_t* packet=0); 971 | uint8_t dmpGet6AxisQuaternion(int16_t *data, const uint8_t* packet=0); 972 | uint8_t dmpGet6AxisQuaternion(Quaternion *q, const uint8_t* packet=0); 973 | uint8_t dmpGetRelativeQuaternion(int32_t *data, const uint8_t* packet=0); 974 | uint8_t dmpGetRelativeQuaternion(int16_t *data, const uint8_t* packet=0); 975 | uint8_t dmpGetRelativeQuaternion(Quaternion *data, const uint8_t* packet=0); 976 | uint8_t dmpGetGyro(int32_t *data, const uint8_t* packet=0); 977 | uint8_t dmpGetGyro(int16_t *data, const uint8_t* packet=0); 978 | uint8_t dmpGetGyro(VectorInt16 *v, const uint8_t* packet=0); 979 | uint8_t dmpGetMag(int16_t *data, const uint8_t* packet=0); 980 | uint8_t dmpSetLinearAccelFilterCoefficient(float coef); 981 | uint8_t dmpGetLinearAccel(int32_t *data, const uint8_t* packet=0); 982 | uint8_t dmpGetLinearAccel(int16_t *data, const uint8_t* packet=0); 983 | uint8_t dmpGetLinearAccel(VectorInt16 *v, const uint8_t* packet=0); 984 | uint8_t dmpGetLinearAccel(VectorInt16 *v, VectorInt16 *vRaw, VectorFloat *gravity); 985 | uint8_t dmpGetLinearAccelInWorld(int32_t *data, const uint8_t* packet=0); 986 | uint8_t dmpGetLinearAccelInWorld(int16_t *data, const uint8_t* packet=0); 987 | uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, const uint8_t* packet=0); 988 | uint8_t dmpGetLinearAccelInWorld(VectorInt16 *v, VectorInt16 *vReal, Quaternion *q); 989 | uint8_t dmpGetGyroAndAccelSensor(int32_t *data, const uint8_t* packet=0); 990 | uint8_t dmpGetGyroAndAccelSensor(int16_t *data, const uint8_t* packet=0); 991 | uint8_t dmpGetGyroAndAccelSensor(VectorInt16 *g, VectorInt16 *a, const uint8_t* packet=0); 992 | uint8_t dmpGetGyroSensor(int32_t *data, const uint8_t* packet=0); 993 | uint8_t dmpGetGyroSensor(int16_t *data, const uint8_t* packet=0); 994 | uint8_t dmpGetGyroSensor(VectorInt16 *v, const uint8_t* packet=0); 995 | uint8_t dmpGetControlData(int32_t *data, const uint8_t* packet=0); 996 | uint8_t dmpGetTemperature(int32_t *data, const uint8_t* packet=0); 997 | uint8_t dmpGetGravity(int32_t *data, const uint8_t* packet=0); 998 | uint8_t dmpGetGravity(int16_t *data, const uint8_t* packet=0); 999 | uint8_t dmpGetGravity(VectorInt16 *v, const uint8_t* packet=0); 1000 | uint8_t dmpGetGravity(VectorFloat *v, Quaternion *q); 1001 | uint8_t dmpGetUnquantizedAccel(int32_t *data, const uint8_t* packet=0); 1002 | uint8_t dmpGetUnquantizedAccel(int16_t *data, const uint8_t* packet=0); 1003 | uint8_t dmpGetUnquantizedAccel(VectorInt16 *v, const uint8_t* packet=0); 1004 | uint8_t dmpGetQuantizedAccel(int32_t *data, const uint8_t* packet=0); 1005 | uint8_t dmpGetQuantizedAccel(int16_t *data, const uint8_t* packet=0); 1006 | uint8_t dmpGetQuantizedAccel(VectorInt16 *v, const uint8_t* packet=0); 1007 | uint8_t dmpGetExternalSensorData(int32_t *data, uint16_t size, const uint8_t* packet=0); 1008 | uint8_t dmpGetEIS(int32_t *data, const uint8_t* packet=0); 1009 | 1010 | uint8_t dmpGetEuler(float *data, Quaternion *q); 1011 | uint8_t dmpGetYawPitchRoll(float *data, Quaternion *q, VectorFloat *gravity); 1012 | 1013 | // Get Floating Point data from FIFO 1014 | uint8_t dmpGetAccelFloat(float *data, const uint8_t* packet=0); 1015 | uint8_t dmpGetQuaternionFloat(float *data, const uint8_t* packet=0); 1016 | 1017 | uint8_t dmpProcessFIFOPacket(const unsigned char *dmpData); 1018 | uint8_t dmpReadAndProcessFIFOPacket(uint8_t numPackets, uint8_t *processed=NULL); 1019 | 1020 | uint8_t dmpSetFIFOProcessedCallback(void (*func) (void)); 1021 | 1022 | uint8_t dmpInitFIFOParam(); 1023 | uint8_t dmpCloseFIFO(); 1024 | uint8_t dmpSetGyroDataSource(uint8_t source); 1025 | uint8_t dmpDecodeQuantizedAccel(); 1026 | uint32_t dmpGetGyroSumOfSquare(); 1027 | uint32_t dmpGetAccelSumOfSquare(); 1028 | void dmpOverrideQuaternion(long *q); 1029 | uint16_t dmpGetFIFOPacketSize(); 1030 | #endif 1031 | 1032 | private: 1033 | uint8_t devAddr; 1034 | uint8_t buffer[14]; 1035 | #if defined(MPU6050_INCLUDE_DMP_MOTIONAPPS20) or defined(MPU6050_INCLUDE_DMP_MOTIONAPPS41) 1036 | uint8_t *dmpPacketBuffer; 1037 | uint16_t dmpPacketSize; 1038 | #endif 1039 | }; 1040 | 1041 | #endif /* _MPU6050_H_ */ 1042 | -------------------------------------------------------------------------------- /lib/MPU6050/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "I2Cdevlib-MPU6050", 3 | "keywords": "gyroscope, accelerometer, sensor, i2cdevlib, i2c", 4 | "description": "The MPU6050 combines a 3-axis gyroscope and a 3-axis accelerometer on the same silicon die together with an onboard Digital Motion Processor(DMP) which processes complex 6-axis MotionFusion algorithms", 5 | "include": "Arduino/MPU6050", 6 | "repository": 7 | { 8 | "type": "git", 9 | "url": "https://github.com/jrowberg/i2cdevlib.git" 10 | }, 11 | "dependencies": 12 | { 13 | "name": "I2Cdevlib-Core", 14 | "frameworks": "arduino" 15 | }, 16 | "frameworks": "arduino", 17 | "platforms": "atmelavr" 18 | } 19 | -------------------------------------------------------------------------------- /lib/MpuHelper/MpuHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "MpuHelper.h" 2 | 3 | void MpuHelperClass::read() { 4 | preferences.begin("mpu", true); 5 | host = preferences.getString("host"); 6 | port = preferences.getInt("port", 3333); 7 | duration = preferences.getInt("duration", 1); 8 | threshold = preferences.getInt("threshold", 20); 9 | preferences.end(); 10 | } 11 | 12 | void MpuHelperClass::write() { 13 | preferences.begin("mpu", false); 14 | preferences.putString("host", host); 15 | preferences.putInt("port", port); 16 | preferences.putInt("duration", duration); 17 | preferences.putInt("threshold", threshold); 18 | preferences.end(); 19 | } 20 | 21 | void MpuHelperClass::setup() { 22 | unsigned long setupStartTime = millis(); 23 | Serial.println("Setup MPU helper"); 24 | 25 | read(); 26 | 27 | Wire.begin(SDA, SCL); 28 | 29 | // Reset the MPU (to try different settings) 30 | // mpu6050.reset(); 31 | // delay(100); 32 | 33 | mpu6050.initialize(); 34 | 35 | Serial.println("Testing device connections..."); 36 | if (!mpu6050.testConnection()) { 37 | Serial.println("MPU6050 connection failed"); 38 | return; 39 | } 40 | 41 | Serial.println("MPU6050 connection successful"); 42 | 43 | // make sure accel is running 44 | mpu6050.setSleepEnabled(false); 45 | mpu6050.setWakeCycleEnabled(false); 46 | 47 | // enable accel 48 | mpu6050.setStandbyXAccelEnabled(false); 49 | mpu6050.setStandbyYAccelEnabled(false); 50 | mpu6050.setStandbyZAccelEnabled(false); 51 | 52 | // standby gyro 53 | mpu6050.setStandbyXGyroEnabled(true); 54 | mpu6050.setStandbyYGyroEnabled(true); 55 | mpu6050.setStandbyZGyroEnabled(true); 56 | 57 | // disable temperature sensor 58 | mpu6050.setTempSensorEnabled(false); 59 | 60 | // set accel HPF to reset settings 61 | mpu6050.setDHPFMode(0); 62 | 63 | // set accel LPF setting to 256hz bandwidth 64 | mpu6050.setDLPFMode(0); 65 | 66 | // enable motion interrupt 67 | mpu6050.setIntMotionEnabled(true); 68 | mpu6050.setMotionDetectionDuration(duration); // 1 69 | mpu6050.setMotionDetectionThreshold(threshold); // 20 70 | 71 | // logSettings(); 72 | 73 | setupDuration = millis() - setupStartTime; 74 | Serial.print("Setup MPU helper took "); 75 | Serial.println(setupDuration); 76 | } 77 | 78 | void MpuHelperClass::loop() { 79 | readValues(); 80 | sendValues(); 81 | } 82 | 83 | void MpuHelperClass::sleep() { 84 | if (!mpu6050.testConnection()) { 85 | return; 86 | } 87 | 88 | // set accel HPF to hold 89 | mpu6050.setDHPFMode(7); 90 | 91 | // set the frequency to wakeup 92 | mpu6050.setWakeFrequency(0); 93 | 94 | mpu6050.setStandbyXGyroEnabled(true); // should be already set 95 | mpu6050.setStandbyYGyroEnabled(true); // should be already set 96 | mpu6050.setStandbyZGyroEnabled(true); // should be already set 97 | 98 | mpu6050.setTempSensorEnabled(false); 99 | mpu6050.setWakeCycleEnabled(true); 100 | 101 | // Write low to MPU pins to fallback to 6.5uA 102 | digitalWrite(SDA, LOW); 103 | digitalWrite(SCL, LOW); 104 | } 105 | 106 | void MpuHelperClass::readValues() { 107 | mpu6050.getMotion6(&ax, &ay, &az, &gx, &gy, &gz); 108 | 109 | Serial.print(ax); 110 | Serial.print("\t"); 111 | Serial.print(ay); 112 | Serial.print("\t"); 113 | Serial.println(az); 114 | 115 | calculateSide(); 116 | } 117 | 118 | int MpuHelperClass::calculateAxis(int16_t &value) { 119 | return value > 8000 ? 1 : value < -8000 ? -1 : 0; 120 | } 121 | 122 | void MpuHelperClass::calculateSide() { 123 | int sideX = calculateAxis(ax); 124 | int sideY = calculateAxis(ay); 125 | int sideZ = calculateAxis(az); 126 | 127 | prevSide = side; 128 | 129 | if (sideX == 0 && sideY == 0 && sideZ == 1) { 130 | side = 0; 131 | } else if (sideX == 0 && sideY == 0 && sideZ == -1) { 132 | side = 1; 133 | } else if (sideX == 0 && sideY == -1 && sideZ == 0) { 134 | side = 2; 135 | } else if (sideX == 0 && sideY == 1 && sideZ == 0) { 136 | side = 3; 137 | } else if (sideX == 1 && sideY == 0 && sideZ == 0) { 138 | side = 4; 139 | } else if (sideX == -1 && sideY == 0 && sideZ == 0) { 140 | side = 5; 141 | } 142 | 143 | // Serial.println(side); 144 | } 145 | 146 | void MpuHelperClass::sendValues() { 147 | if (prevSide == side) { 148 | return; 149 | } 150 | if (host.length() == 0) return; 151 | if (!WiFiHelper.connect()) return; 152 | unsigned long requestStartTime = millis(); 153 | 154 | StaticJsonDocument<32> doc; 155 | 156 | udp.beginPacket(host.c_str(), port); 157 | 158 | doc["side"] = side; 159 | serializeJson(doc, udp); 160 | 161 | udp.println(); 162 | udp.endPacket(); 163 | 164 | requestDuration = millis() - requestStartTime; 165 | Serial.print("Send UDP request "); 166 | Serial.println(requestDuration); 167 | } 168 | 169 | void MpuHelperClass::logSettings() { 170 | Serial.print("WakeCycle "); 171 | Serial.println(String(mpu6050.getWakeCycleEnabled())); 172 | 173 | Serial.print("DHPFMode "); 174 | Serial.println(String(mpu6050.getDHPFMode())); 175 | Serial.print("DLPFMode "); 176 | Serial.println(String(mpu6050.getDLPFMode())); 177 | 178 | Serial.println("AccelStandby "); 179 | Serial.println(String(mpu6050.getStandbyXAccelEnabled())); 180 | 181 | Serial.print("GyroStandby "); 182 | Serial.println(String(mpu6050.getStandbyXGyroEnabled())); 183 | Serial.print("TempSensor "); 184 | Serial.println(String(mpu6050.getTempSensorEnabled())); 185 | Serial.println(); 186 | 187 | Serial.print("FIFO Enabled "); 188 | Serial.println(String(mpu6050.getFIFOEnabled())); 189 | Serial.print("XGyroFifo Enabled "); 190 | Serial.println(String(mpu6050.getXGyroFIFOEnabled())); 191 | Serial.print("IntFIFOBufferOverflow Enabled "); 192 | Serial.println(String(mpu6050.getIntFIFOBufferOverflowEnabled())); 193 | Serial.print("Clock Source "); 194 | Serial.println(String(mpu6050.getClockSource())); 195 | Serial.print("FullScalAccelRange "); 196 | Serial.println(String(mpu6050.getFullScaleAccelRange())); 197 | Serial.print("FullScalGyroRange "); 198 | Serial.println(String(mpu6050.getFullScaleGyroRange())); 199 | 200 | Serial.println(); 201 | } 202 | 203 | void MpuHelperClass::server() { 204 | Serial.println("Setup MPU server"); 205 | 206 | WebServerHelper.server.on("/api/mpu", HTTP_GET, [this](AsyncWebServerRequest *request) { 207 | int args = request->args(); 208 | 209 | if (args > 0) { 210 | request->send(200, "text/plain", "message received"); 211 | Serial.println("Update mpu settings"); 212 | 213 | if (request->hasArg("host")) host = request->arg("host"); 214 | if (request->hasArg("port")) port = request->arg("port").toInt(); 215 | if (request->hasArg("duration")) duration = request->arg("duration").toInt(); 216 | if (request->hasArg("threshold")) threshold = request->arg("threshold").toInt(); 217 | 218 | write(); 219 | 220 | } else { 221 | AsyncJsonResponse *response = new AsyncJsonResponse(); 222 | response->addHeader("Server", "ESP Async Web Server"); 223 | JsonVariant &root = response->getRoot(); 224 | 225 | root["side"] = side; 226 | root["setupDuration"] = setupDuration; 227 | root["requestDuration"] = requestDuration; 228 | 229 | root["host"] = host; 230 | root["port"] = port; 231 | 232 | root["duration"] = duration; 233 | root["threshold"] = threshold; 234 | 235 | response->setLength(); 236 | request->send(response); 237 | } 238 | }); 239 | } 240 | 241 | MpuHelperClass MpuHelper; 242 | -------------------------------------------------------------------------------- /lib/MpuHelper/MpuHelper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MpuHelper_h 3 | #define MpuHelper_h 4 | 5 | #include "Arduino.h" 6 | #include "ArduinoJson.h" 7 | #include "AsyncJson.h" 8 | #include "I2Cdev.h" 9 | #include "MPU6050.h" 10 | #include "Preferences.h" 11 | #include "WebServerHelper.h" 12 | #include "WiFiHelper.h" 13 | #include "WiFiUdp.h" 14 | #include "Wire.h" 15 | 16 | class MpuHelperClass { 17 | private: 18 | WiFiUDP udp; 19 | MPU6050 mpu6050; 20 | Preferences preferences; 21 | 22 | int16_t ax, ay, az; 23 | int16_t gx, gy, gz; 24 | 25 | String host; 26 | int32_t port; 27 | 28 | void read(); 29 | void write(); 30 | 31 | int calculateAxis(int16_t &value); 32 | void calculateSide(); 33 | void logSettings(); 34 | void readValues(); 35 | void sendValues(); 36 | 37 | unsigned long requestDuration; 38 | unsigned long setupDuration; 39 | 40 | int duration; 41 | int threshold; 42 | 43 | public: 44 | int prevSide; 45 | int side; 46 | 47 | void setup(); 48 | void server(); 49 | void loop(); 50 | void sleep(); 51 | }; 52 | 53 | extern MpuHelperClass MpuHelper; 54 | 55 | #endif -------------------------------------------------------------------------------- /lib/MpuHelper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "MpuHelper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /lib/WaveshareHelper/WaveshareHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "WaveshareHelper.h" 2 | 3 | static const uint8_t EPD_BUSY = 4; // to EPD BUSY 4 | static const uint8_t EPD_CS = 5; // to EPD CS 5 | static const uint8_t EPD_RST = 16; // to EPD RST 6 | static const uint8_t EPD_DC = 17; // to EPD DC 7 | static const uint8_t EPD_SCK = 18; // to EPD CLK 8 | static const uint8_t EPD_MISO = 19; // Master-In Slave-Out not used, as no data from display 9 | static const uint8_t EPD_MOSI = 23; // to EPD DIN 10 | 11 | GxEPD2_BW display(GxEPD2_750_T7(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)); 12 | U8G2_FOR_ADAFRUIT_GFX u8g2Fonts; // https://github.com/olikraus/u8g2/wiki/fntlistall 13 | 14 | void WaveshareHelperClass::read() { 15 | preferences.begin("waveshare", true); 16 | host = preferences.getString("host"); 17 | user = preferences.getString("user", "display"); 18 | password = preferences.getString("password", "display"); 19 | updateInterval = preferences.getInt("updateInterval", 30); 20 | preferences.end(); 21 | } 22 | 23 | void WaveshareHelperClass::write() { 24 | preferences.begin("waveshare", false); 25 | preferences.putString("host", host); 26 | preferences.putString("user", user); 27 | preferences.putString("password", password); 28 | preferences.putInt("updateInterval", updateInterval); 29 | preferences.end(); 30 | } 31 | 32 | void WaveshareHelperClass::setup() { 33 | unsigned long setupStartTime = millis(); 34 | Serial.println("Setup Waveshare helper"); 35 | 36 | read(); 37 | initDisplay(); 38 | 39 | setupDuration = millis() - setupStartTime; 40 | Serial.print("Setup Waveshare helper took "); 41 | Serial.println(setupDuration); 42 | } 43 | 44 | void WaveshareHelperClass::initDisplay() { 45 | display.init(115200, true, 2); 46 | 47 | SPI.end(); 48 | SPI.begin(EPD_SCK, EPD_MISO, EPD_MOSI, EPD_CS); 49 | u8g2Fonts.begin(display); // connect u8g2 procedures to Adafruit GFX 50 | u8g2Fonts.setFontMode(1); // use u8g2 transparent mode (this is default) 51 | u8g2Fonts.setFontDirection(0); // left to right (this is default) 52 | u8g2Fonts.setForegroundColor(GxEPD_BLACK); 53 | u8g2Fonts.setBackgroundColor(GxEPD_WHITE); 54 | u8g2Fonts.setFont(u8g2_font_helvB10_tf); // select u8g2 font from here: https://github.com/olikraus/u8g2/wiki/fntlistall 55 | display.fillScreen(GxEPD_WHITE); 56 | display.setFullWindow(); 57 | } 58 | 59 | void WaveshareHelperClass::update(bool disconnect) { 60 | if (host.length() == 0) return; 61 | 62 | u8g2Fonts.setBackgroundColor(GxEPD_BLACK); 63 | u8g2Fonts.setForegroundColor(GxEPD_WHITE); 64 | display.fillRect(0, 0, 320, 480, GxEPD_BLACK); 65 | 66 | drawStatus("CO2", "/jdev/sps/io/CO2Status", 40, 80); 67 | drawStatus("Qualität", "/jdev/sps/io/IAQStatus", 40, 200); 68 | drawStatus("Feuchtigkeit", "/jdev/sps/io/HumidityStatus", 40, 320); 69 | 70 | u8g2Fonts.setBackgroundColor(GxEPD_WHITE); 71 | u8g2Fonts.setForegroundColor(GxEPD_BLACK); 72 | 73 | drawUsage("Allgemein", "/jdev/sps/io/AVerbrauch/all", 360, 30); 74 | drawUsage("Heizung", "/jdev/sps/io/HVerbrauch/all", 360, 270); 75 | 76 | if (disconnect) { 77 | WiFi.disconnect(); 78 | WiFi.mode(WIFI_OFF); 79 | } 80 | 81 | display.display(false); // Full screen update mode 82 | } 83 | 84 | void WaveshareHelperClass::drawStatus(String title, String urlPart, int offsetX, int offsetY) { 85 | DynamicJsonDocument doc(2048); 86 | if (!getAndParse(doc, urlPart)) return; 87 | 88 | // Read values 89 | String statusText = doc["LL"]["value"]; 90 | 91 | u8g2Fonts.setFont(u8g2_font_helvB10_tf); 92 | drawString(offsetX, offsetY, title, LEFT); 93 | 94 | u8g2Fonts.setFont(u8g2_font_helvB24_tf); 95 | drawString(offsetX, offsetY + 40, statusText, LEFT); 96 | } 97 | 98 | void WaveshareHelperClass::drawUsage(String title, String urlPart, int offsetX, int offsetY) { 99 | DynamicJsonDocument doc(2048); 100 | if (!getAndParse(doc, urlPart)) return; 101 | 102 | u8g2Fonts.setFont(u8g2_font_helvB14_tf); 103 | drawString(offsetX, offsetY, title, LEFT); 104 | 105 | // Read values 106 | float usageC = doc["LL"]["output1"]["value"].as() * 1000; 107 | float usage0 = doc["LL"]["output3"]["value"].as(); 108 | float usage1 = doc["LL"]["output4"]["value"].as(); 109 | float usage2 = doc["LL"]["output5"]["value"].as(); 110 | 111 | u8g2Fonts.setFont(u8g2_font_helvR24_tf); 112 | drawString(290 + offsetX, 120 + offsetY, String(usageC, 0) + " W", LEFT); 113 | 114 | u8g2Fonts.setFont(u8g2_font_helvB08_tf); 115 | drawString(290 + offsetX, 85 + offsetY, "Aktuell", LEFT); 116 | 117 | drawString(45 + offsetX, 50 + offsetY, "Vorgestern", CENTER); 118 | drawString(135 + offsetX, 50 + offsetY, "Gestern", CENTER); 119 | drawString(225 + offsetX, 50 + offsetY, "Heute", CENTER); 120 | 121 | display.drawLine(10 + offsetX, 150 + offsetY, 260 + offsetX, 150 + offsetY, GxEPD_BLACK); 122 | 123 | u8g2Fonts.setFont(u8g2_font_helvR14_tf); 124 | drawString(40 + offsetX, 170 + offsetY, String(usage2), CENTER); 125 | drawString(130 + offsetX, 170 + offsetY, String(usage1), CENTER); 126 | drawString(225 + offsetX, 170 + offsetY, String(usage0), CENTER); 127 | 128 | float maxUsage = max(usage2, usage1); 129 | maxUsage = max(maxUsage, usage0) / 100; 130 | 131 | float pUsage0 = usage0 / maxUsage * 0.7; 132 | float pUsage1 = usage1 / maxUsage * 0.7; 133 | float pUsage2 = usage2 / maxUsage * 0.7; 134 | 135 | display.fillRect(35 + offsetX, 151 + offsetY - pUsage2, 20, pUsage2, GxEPD_BLACK); 136 | display.fillRect(125 + offsetX, 151 + offsetY - pUsage1, 20, pUsage1, GxEPD_BLACK); 137 | display.fillRect(215 + offsetX, 151 + offsetY - pUsage0, 20, pUsage0, GxEPD_BLACK); 138 | } 139 | 140 | bool WaveshareHelperClass::getAndParse(JsonDocument &doc, String urlPart) { 141 | http.useHTTP10(true); 142 | http.begin(host + urlPart); 143 | http.setAuthorization(user.c_str(), password.c_str()); 144 | int httpCode = http.GET(); 145 | 146 | if (httpCode != HTTP_CODE_OK) { 147 | Serial.print("http error code: "); 148 | Serial.println(httpCode); 149 | return false; 150 | } 151 | 152 | DeserializationError error = deserializeJson(doc, http.getStream()); 153 | 154 | http.end(); 155 | 156 | // Test if parsing succeeds. 157 | if (error) { 158 | Serial.print(F("deserializeJson failed: ")); 159 | Serial.println(error.c_str()); 160 | return false; 161 | } 162 | 163 | return true; 164 | } 165 | 166 | void WaveshareHelperClass::drawString(int x, int y, String text, alignment align) { 167 | int16_t x1, y1; 168 | uint16_t w, h; 169 | 170 | display.setTextWrap(false); 171 | display.getTextBounds(text, x, y, &x1, &y1, &w, &h); 172 | 173 | if (align == RIGHT) x = x - w; 174 | if (align == CENTER) x = x - w / 2; 175 | 176 | u8g2Fonts.setCursor(x, y + h); 177 | u8g2Fonts.print(text); 178 | } 179 | 180 | void WaveshareHelperClass::sleep() { 181 | display.powerOff(); 182 | 183 | // Write low to reset 184 | digitalWrite(SDA, LOW); 185 | digitalWrite(SCL, LOW); 186 | 187 | pinMode(BUILTIN_LED, INPUT); // If it's On, turn it off and some boards use GPIO-5 for SPI-SS, which remains low after screen use 188 | digitalWrite(BUILTIN_LED, HIGH); 189 | 190 | long wakeupTime = uS_TO_S_FACTOR * 60UL * long(updateInterval); 191 | esp_sleep_enable_timer_wakeup(wakeupTime); 192 | esp_deep_sleep_start(); 193 | } 194 | 195 | void WaveshareHelperClass::server() { 196 | Serial.println("Setup Waveshare server"); 197 | 198 | WebServerHelper.server.on("/api/waveshare", HTTP_GET, [this](AsyncWebServerRequest *request) { 199 | int args = request->args(); 200 | 201 | if (args > 0) { 202 | request->send(200, "text/plain", "message received"); 203 | Serial.println("Update Waveshare settings"); 204 | 205 | if (request->hasArg("host")) host = request->arg("host"); 206 | if (request->hasArg("user")) user = request->arg("user"); 207 | if (request->hasArg("password")) password = request->arg("password").toInt(); 208 | if (request->hasArg("updateInterval")) updateInterval = request->arg("updateInterval").toInt(); 209 | 210 | write(); 211 | ESP.restart(); 212 | 213 | } else { 214 | AsyncJsonResponse *response = new AsyncJsonResponse(); 215 | response->addHeader("Server", "ESP Async Web Server"); 216 | JsonVariant &root = response->getRoot(); 217 | 218 | root["setupDuration"] = setupDuration; 219 | root["host"] = host; 220 | root["user"] = user; 221 | root["updateInterval"] = updateInterval; 222 | 223 | response->setLength(); 224 | request->send(response); 225 | } 226 | }); 227 | } 228 | 229 | WaveshareHelperClass WaveshareHelper; 230 | -------------------------------------------------------------------------------- /lib/WaveshareHelper/WaveshareHelper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef WaveshareHelper_h 3 | #define WaveshareHelper_h 4 | #define ENABLE_GxEPD2_display 1 5 | 6 | #include "ArduinoJson.h" 7 | #include "AsyncJson.h" 8 | #include "GxEPD2_BW.h" 9 | // #include "I2Cdev.h" 10 | #include "HTTPClient.h" 11 | #include "SPI.h" 12 | #include "U8g2_for_Adafruit_GFX.h" 13 | #include "WebServerHelper.h" 14 | #include "WiFi.h" 15 | #include "Wire.h" 16 | 17 | #define SCREEN_WIDTH 800 18 | #define SCREEN_HEIGHT 480 19 | #define uS_TO_S_FACTOR 1000000 20 | 21 | enum alignment { LEFT, 22 | RIGHT, 23 | CENTER }; 24 | 25 | class WaveshareHelperClass { 26 | private: 27 | Preferences preferences; 28 | HTTPClient http; 29 | String host; 30 | String user; 31 | String password; 32 | int32_t updateInterval; 33 | 34 | void read(); 35 | void write(); 36 | 37 | void readValues(); 38 | void sendValues(); 39 | 40 | unsigned long setupDuration; 41 | 42 | void initDisplay(); 43 | 44 | bool getAndParse(JsonDocument &doc, String urlPart); 45 | void drawStatus(String title, String urlPart, int offsetX, int offsetY); 46 | void drawUsage(String title, String urlPart, int offsetX, int offsetY); 47 | void drawString(int x, int y, String text, alignment align); 48 | 49 | public: 50 | void setup(); 51 | void server(); 52 | void update(bool disconnect); 53 | void sleep(); 54 | }; 55 | 56 | extern WaveshareHelperClass WaveshareHelper; 57 | 58 | #endif -------------------------------------------------------------------------------- /lib/WaveshareHelper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WaveshareHelper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /lib/WebServerHelper/WebServerHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "WebServerHelper.h" 2 | 3 | WebServerHelperClass::WebServerHelperClass() : server(80){}; 4 | 5 | void WebServerHelperClass::start(String application) { 6 | Serial.println("WebServer start"); 7 | 8 | app = application; 9 | 10 | if (!SPIFFS.begin()) { 11 | Serial.println("An Error has occurred while mounting SPIFFS"); 12 | return; 13 | } 14 | 15 | server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { 16 | request->send(SPIFFS, "/index.html", "text/html"); 17 | }); 18 | 19 | server.on("/bundle.js", HTTP_GET, [](AsyncWebServerRequest *request) { 20 | request->send(SPIFFS, "/bundle.js"); 21 | }); 22 | 23 | server.on("/api/restart", HTTP_GET, [](AsyncWebServerRequest *request) { 24 | request->send(200, "text/plain", "message received"); 25 | ESP.restart(); 26 | }); 27 | 28 | server.on("/api/sleep", HTTP_GET, [this](AsyncWebServerRequest *request) { 29 | request->send(200, "text/plain", "message received"); 30 | 31 | if (sleep) { 32 | sleep(); 33 | } 34 | }); 35 | 36 | WebServerHelper.server.on("/api/application", HTTP_GET, [this](AsyncWebServerRequest *request) { 37 | AsyncJsonResponse *response = new AsyncJsonResponse(); 38 | response->addHeader("Server", "ESP Async Web Server"); 39 | 40 | JsonVariant &root = response->getRoot(); 41 | root["application"] = app; 42 | 43 | response->setLength(); 44 | request->send(response); 45 | }); 46 | 47 | server.on("/api/esp", HTTP_GET, [this](AsyncWebServerRequest *request) { 48 | AsyncJsonResponse *response = new AsyncJsonResponse(); 49 | response->addHeader("Server", "ESP Async Web Server"); 50 | 51 | JsonVariant &root = response->getRoot(); 52 | root["heap"] = ESP.getFreeHeap(); 53 | root["cupFreq"] = ESP.getCpuFreqMHz(); 54 | root["sketchSize"] = ESP.getSketchSize(); 55 | root["sketchSizeFree"] = ESP.getFreeSketchSpace(); 56 | 57 | response->setLength(); 58 | request->send(response); 59 | }); 60 | 61 | server.on( 62 | "/api/update", HTTP_POST, [this](AsyncWebServerRequest *request) { 63 | request->send_P(200, "text/plain", "message received"); 64 | ESP.restart(); }, 65 | [this](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { 66 | uploadFile(U_FLASH, request, filename, index, data, len, final); 67 | }); 68 | 69 | server.on( 70 | "/api/updateFS", HTTP_POST, [this](AsyncWebServerRequest *request) { 71 | request->send_P(200, "text/plain", "message received"); 72 | ESP.restart(); }, 73 | [this](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { 74 | uploadFile(U_SPIFFS, request, filename, index, data, len, final); 75 | }); 76 | 77 | server.begin(); 78 | Serial.println("WebServer running"); 79 | } 80 | 81 | void WebServerHelperClass::uploadFile(int command, AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) { 82 | if (!index) { 83 | Serial.printf("UploadStart: %s\n", filename.c_str()); 84 | if (!Update.begin(UPDATE_SIZE_UNKNOWN, command)) { 85 | Update.printError(Serial); 86 | } 87 | } 88 | 89 | if (!Update.hasError()) { 90 | if (Update.write(data, len) != len) { 91 | Update.printError(Serial); 92 | } 93 | } 94 | 95 | if (final) { 96 | if (Update.end(true)) { 97 | Serial.printf("Update Success: %uB\n", index + len); 98 | } else { 99 | Update.printError(Serial); 100 | } 101 | } 102 | } 103 | 104 | void WebServerHelperClass::onSleep(SleepFunc sleepFn) { 105 | sleep = sleepFn; 106 | } 107 | 108 | WebServerHelperClass WebServerHelper; 109 | -------------------------------------------------------------------------------- /lib/WebServerHelper/WebServerHelper.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef WebServerHelper_h 3 | #define WebServerHelper_h 4 | 5 | #include "ArduinoJson.h" 6 | #include "AsyncJson.h" 7 | #include "ESPAsyncWebServer.h" 8 | #include "SPIFFS.h" 9 | #include "Update.h" 10 | 11 | class WebServerHelperClass { 12 | private: 13 | void uploadFile(int command, AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final); 14 | 15 | public: 16 | String app; 17 | AsyncWebServer server; 18 | WebServerHelperClass(); 19 | void start(String application); 20 | 21 | using SleepFunc = void (*)(); 22 | SleepFunc sleep; 23 | void onSleep(SleepFunc sleep = nullptr); 24 | }; 25 | 26 | extern WebServerHelperClass WebServerHelper; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /lib/WebServerHelper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WebServerHelper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /lib/WiFiHelper/WiFiHelper.cpp: -------------------------------------------------------------------------------- 1 | #include "WiFiHelper.h" 2 | 3 | void WiFiHelperClass::read() { 4 | preferences.begin("esp", true); // create folder 5 | 6 | ssid = preferences.getString("ssid", DEFAULT_SSID); 7 | password = preferences.getString("password", DEFAULT_PSK); 8 | host = preferences.getString("host", DEFAULT_HOST); 9 | 10 | mode = preferences.getString("mode", "dhcp"); 11 | ipv4 = preferences.getString("ipv4"); 12 | dns = preferences.getString("dns"); 13 | subnet = preferences.getString("subnet"); 14 | gateway = preferences.getString("gateway"); 15 | 16 | preferences.end(); 17 | } 18 | 19 | void WiFiHelperClass::write() { 20 | preferences.begin("esp", false); // create folder 21 | 22 | preferences.putString("ssid", ssid); 23 | preferences.putString("password", password); 24 | preferences.putString("host", host); 25 | 26 | preferences.putString("mode", mode); 27 | preferences.putString("ipv4", ipv4); 28 | preferences.putString("dns", dns); 29 | preferences.putString("subnet", subnet); 30 | preferences.putString("gateway", gateway); 31 | 32 | preferences.end(); 33 | } 34 | 35 | void WiFiHelperClass::setup() { 36 | read(); 37 | } 38 | 39 | bool WiFiHelperClass::connect(bool firstConnect) { 40 | if (WiFi.status() == WL_CONNECTED) return true; 41 | 42 | unsigned long wifiStartTime = millis(); 43 | 44 | if (mode == "static") { 45 | IPAddress ipv4IP; 46 | IPAddress dnsIP; 47 | IPAddress subnetIP; 48 | IPAddress gatewayIP; 49 | 50 | ipv4IP.fromString(ipv4); 51 | dnsIP.fromString(dns); 52 | subnetIP.fromString(subnet); 53 | gatewayIP.fromString(gateway); 54 | 55 | Serial.print("WIFI use static IP: "); 56 | Serial.println(ipv4); 57 | 58 | WiFi.config(ipv4IP, gatewayIP, subnetIP, dnsIP); 59 | } 60 | 61 | Serial.print("WIFI connect to "); 62 | Serial.println(ssid); 63 | 64 | WiFi.mode(WIFI_STA); 65 | 66 | WiFi.setSleep(true); 67 | WiFi.begin(ssid.c_str(), password.c_str()); 68 | 69 | // The first connect can take a while 70 | // use the built in function to wait (up to 10sec) 71 | if (firstConnect) { 72 | WiFi.waitForConnectResult(); 73 | } else { 74 | int retry = 0; 75 | while (WiFi.status() != WL_CONNECTED && retry++ < 100) { 76 | delay(10); 77 | } 78 | } 79 | 80 | wifiTime = millis() - wifiStartTime; 81 | Serial.print("WIFI connection took "); 82 | Serial.println(String(wifiTime)); 83 | 84 | return WiFi.status() == WL_CONNECTED; 85 | } 86 | 87 | void WiFiHelperClass::setupAP() { 88 | // configure WiFi AP 89 | Serial.println("WIFI Setup AP"); 90 | 91 | WiFi.disconnect(); 92 | WiFi.softAP("espbs", "password"); 93 | 94 | IPAddress myIP = WiFi.softAPIP(); 95 | Serial.print("AP IP address: "); 96 | Serial.println(myIP.toString()); 97 | } 98 | 99 | void WiFiHelperClass::setupMDNS() { 100 | // use mdns for host name resolution 101 | if (!MDNS.begin(host.c_str())) { 102 | Serial.println("Error setting up MDNS responder!"); 103 | } 104 | Serial.print("mDNS responder started connect to http://"); 105 | Serial.print(host); 106 | Serial.println(".local"); 107 | } 108 | 109 | void WiFiHelperClass::sleep() { 110 | WiFi.disconnect(true); 111 | WiFi.mode(WIFI_OFF); 112 | 113 | btStop(); 114 | 115 | adc_power_off(); 116 | esp_wifi_stop(); 117 | } 118 | 119 | void WiFiHelperClass::server() { 120 | Serial.println("Setup WiFi helper"); 121 | 122 | if (WiFiHelper.connect(true)) { 123 | WiFiHelper.setupMDNS(); 124 | } else { 125 | WiFiHelper.setupAP(); 126 | } 127 | 128 | WebServerHelper.server.on("/api/wifi", HTTP_GET, [this](AsyncWebServerRequest *request) { 129 | int args = request->args(); 130 | 131 | if (args > 0) { 132 | request->send(200, "text/plain", "message received"); 133 | 134 | Serial.println("Update wifi settings"); 135 | if (request->hasArg("host")) host = request->arg("host"); 136 | if (request->hasArg("ssid")) ssid = request->arg("ssid"); 137 | if (request->hasArg("password")) password = request->arg("password"); 138 | 139 | write(); 140 | WiFi.disconnect(); 141 | ESP.restart(); 142 | 143 | } else { 144 | AsyncJsonResponse *response = new AsyncJsonResponse(); 145 | response->addHeader("Server", "ESP Async Web Server"); 146 | 147 | JsonVariant &root = response->getRoot(); 148 | root["host"] = host; 149 | root["ssid"] = ssid; 150 | 151 | root["rssi"] = String(WiFi.RSSI()); 152 | root["time"] = String(wifiTime); 153 | 154 | response->setLength(); 155 | request->send(response); 156 | } 157 | }); 158 | 159 | WebServerHelper.server.on("/api/network", HTTP_GET, [this](AsyncWebServerRequest *request) { 160 | int args = request->args(); 161 | 162 | if (args > 0) { 163 | request->send(200, "text/plain", "message received"); 164 | 165 | Serial.println("Update network settings"); 166 | if (request->hasArg("mode")) mode = request->arg("mode"); 167 | if (request->hasArg("ipv4")) ipv4 = request->arg("ipv4"); 168 | if (request->hasArg("dns")) dns = request->arg("dns"); 169 | if (request->hasArg("subnet")) subnet = request->arg("subnet"); 170 | if (request->hasArg("gateway")) gateway = request->arg("gateway"); 171 | 172 | write(); 173 | WiFi.disconnect(); 174 | ESP.restart(); 175 | } else { 176 | AsyncJsonResponse *response = new AsyncJsonResponse(); 177 | response->addHeader("Server", "ESP Async Web Server"); 178 | 179 | JsonVariant &root = response->getRoot(); 180 | root["mode"] = mode; 181 | root["ipv4"] = WiFi.localIP().toString(); 182 | root["dns"] = WiFi.dnsIP().toString(); 183 | root["subnet"] = WiFi.subnetMask().toString(); 184 | root["gateway"] = WiFi.gatewayIP().toString(); 185 | 186 | response->setLength(); 187 | request->send(response); 188 | } 189 | }); 190 | } 191 | 192 | WiFiHelperClass WiFiHelper; -------------------------------------------------------------------------------- /lib/WiFiHelper/WiFiHelper.h: -------------------------------------------------------------------------------- 1 | #ifndef WiFiHelper_h 2 | #define WiFiHelper_h 3 | 4 | #include "AsyncJson.h" 5 | #include "ESPmDNS.h" 6 | #include "Preferences.h" 7 | #include "WebServerHelper.h" 8 | #include "WiFi.h" 9 | #include "WiFiAP.h" 10 | #include "WiFiClient.h" 11 | #include "driver/adc.h" 12 | #include "esp_wifi.h" 13 | 14 | #define DEFAULT_HOST "esp32" 15 | #define DEFAULT_SSID "ESP32" 16 | #define DEFAULT_PSK "password" 17 | 18 | class WiFiHelperClass { 19 | private: 20 | unsigned long wifiTime; 21 | Preferences preferences; 22 | 23 | String ssid; 24 | String password; 25 | String host; 26 | 27 | String mode; 28 | String ipv4; 29 | String dns; 30 | String subnet; 31 | String gateway; 32 | 33 | void read(); 34 | void write(); 35 | 36 | void setupAP(); 37 | void setupMDNS(); 38 | 39 | public: 40 | void setup(); 41 | void server(); 42 | bool connect(bool firstConnect = false); 43 | void sleep(); 44 | }; 45 | 46 | extern WiFiHelperClass WiFiHelper; 47 | 48 | #endif -------------------------------------------------------------------------------- /lib/WiFiHelper/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WiFiHelper", 3 | "version": "1.0.0" 4 | } -------------------------------------------------------------------------------- /partitions_custom.csv: -------------------------------------------------------------------------------- 1 | # Name, Type, SubType, Offset, Size, Flags 2 | nvs, data, nvs, 0x9000, 0x5000, 3 | otadata, data, ota, 0xe000, 0x2000, 4 | app0, app, ota_0, 0x10000, 0x160000, 5 | app1, app, ota_1, 0x170000,0x160000, 6 | spiffs, data, spiffs, 0x2D0000,0x130000, 7 | 8 | 9 | # default partition table 10 | # Name, Type, SubType, Offset, Size, Flags 11 | # nvs, data, nvs, 0x9000, 0x5000, 12 | # otadata, data, ota, 0xe000, 0x2000, 13 | # app0, app, ota_0, 0x10000, 0x140000, 14 | # app1, app, ota_1, 0x150000,0x140000, 15 | # spiffs, data, spiffs, 0x290000,0x170000, -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [platformio] 12 | description = ESPBS Base system 13 | 14 | [env:lolin_d32] 15 | platform = espressif32 16 | board = lolin_d32 17 | framework = arduino 18 | monitor_speed = 115200 19 | board_build.partitions = partitions_custom.csv 20 | ; build_flags = -D CONFIG_FREERTOS_UNICORE 21 | board_build.f_cpu = 240000000L 22 | 23 | ; ---- CUBE ---- 24 | ;src_filter = + 25 | ;lib_deps = FS, ESP Async WebServer, ArduinoJson, ESPAsyncTCP, Update 26 | 27 | ; ---- CO2 ---- 28 | src_filter = + 29 | lib_deps = FS, ESP Async WebServer, ArduinoJson, ESPAsyncTCP, Update, MH-Z19@1.5.3, BSEC Software Library 30 | build_flags = 31 | -I .pio/libdeps/BSECSoftwareLibrary/src/bme680 32 | -L .pio/libdeps/BSECSoftwareLibrary/src/esp32 33 | -lalgobsec 34 | 35 | ; ---- AqiLed ---- 36 | ; src_filter = + 37 | ; lib_deps = FS, ESP Async WebServer, ArduinoJson, ESPAsyncTCP, Update, BSEC Software Library, fastled/FastLED @ ^3.3.3 38 | ; build_flags = 39 | ; -I .pio/libdeps/BSECSoftwareLibrary/src/bme680 40 | ; -L .pio/libdeps/BSECSoftwareLibrary/src/esp32 41 | ; -lalgobsec 42 | 43 | ; ---- Waveshare display ---- 44 | ; src_filter = + 45 | ; lib_deps = FS, ESP Async WebServer, ArduinoJson, ESPAsyncTCP, Update, GxEPD2, U8g2_for_Adafruit_GFX 46 | 47 | ; ---- Knob ---- 48 | ; src_filter = + 49 | ; lib_deps = FS, ESP Async WebServer, ArduinoJson, ESPAsyncTCP, Update, igorantolic/Ai Esp32 Rotary Encoder @ ^1.0 50 | -------------------------------------------------------------------------------- /screenshots/aqiLed_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/aqiLed_wiring.png -------------------------------------------------------------------------------- /screenshots/aqiled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/aqiled.png -------------------------------------------------------------------------------- /screenshots/co2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/co2.png -------------------------------------------------------------------------------- /screenshots/co2_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/co2_wiring.png -------------------------------------------------------------------------------- /screenshots/cube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/cube.png -------------------------------------------------------------------------------- /screenshots/cube_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/cube_wiring.png -------------------------------------------------------------------------------- /screenshots/display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/display.png -------------------------------------------------------------------------------- /screenshots/display_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/display_wiring.png -------------------------------------------------------------------------------- /screenshots/firmware.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/firmware.png -------------------------------------------------------------------------------- /screenshots/knob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/knob.png -------------------------------------------------------------------------------- /screenshots/knob_wiring.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/knob_wiring.png -------------------------------------------------------------------------------- /screenshots/mpu6050.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/mpu6050.png -------------------------------------------------------------------------------- /screenshots/sleep.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/sleep.png -------------------------------------------------------------------------------- /screenshots/updateWebsite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/updateWebsite.png -------------------------------------------------------------------------------- /screenshots/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/upload.png -------------------------------------------------------------------------------- /screenshots/website.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/website.png -------------------------------------------------------------------------------- /screenshots/wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lillifee/espbs/15b17905f2afefe52391940dabd8823d520f9098/screenshots/wifi.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This file was automatically generated for projects 2 | # without default 'CMakeLists.txt' file. 3 | 4 | FILE(GLOB_RECURSE app_sources ${CMAKE_SOURCE_DIR}/src/*.*) 5 | 6 | idf_component_register(SRCS ${app_sources}) 7 | -------------------------------------------------------------------------------- /src/aqiLed.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "BsecHelper.h" 3 | #include "LedHelper.h" 4 | #include "WebServerHelper.h" 5 | #include "WifiHelper.h" 6 | 7 | void setup() { 8 | Serial.begin(115200); 9 | 10 | WiFiHelper.setup(); 11 | WiFiHelper.server(); 12 | 13 | LedHelper.setup(); 14 | LedHelper.server(); 15 | 16 | BsecHelper.setup(BME680_I2C_ADDR_SECONDARY); 17 | BsecHelper.server(); 18 | 19 | WebServerHelper.start("aqiLed"); 20 | } 21 | 22 | void loop() { 23 | // Stop bsec readings during animation or test 24 | if (LedHelper.animation == 0 && LedHelper.test == 0) { 25 | BsecHelper.loop(); 26 | } 27 | 28 | LedHelper.value = BsecHelper.iaqSensor.iaq + 0.5; 29 | LedHelper.loop(); 30 | } 31 | -------------------------------------------------------------------------------- /src/co2.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "BsecHelper.h" 3 | #include "Co2Helper.h" 4 | #include "WebServerHelper.h" 5 | #include "WifiHelper.h" 6 | 7 | void setup() { 8 | Serial.begin(115200); 9 | 10 | WiFiHelper.setup(); 11 | WiFiHelper.server(); 12 | 13 | Co2Helper.setup(); 14 | Co2Helper.server(); 15 | 16 | BsecHelper.setup(BME680_I2C_ADDR_PRIMARY); 17 | BsecHelper.server(); 18 | 19 | WebServerHelper.start("co2"); 20 | } 21 | 22 | void loop() { 23 | Co2Helper.loop(); 24 | BsecHelper.loop(); 25 | } 26 | -------------------------------------------------------------------------------- /src/cube.cpp: -------------------------------------------------------------------------------- 1 | #include "MpuHelper.h" 2 | #include "WebServerHelper.h" 3 | #include "WifiHelper.h" 4 | #include "rom/rtc.h" 5 | 6 | #define LED_BUILTIN 5 7 | unsigned long sleepTime; 8 | RESET_REASON resetReason; 9 | 10 | void deepSleep() { 11 | WiFiHelper.sleep(); 12 | MpuHelper.sleep(); 13 | 14 | esp_sleep_enable_ext0_wakeup(GPIO_NUM_33, 1); 15 | esp_deep_sleep_start(); 16 | } 17 | 18 | void setup() { 19 | Serial.begin(115200); 20 | 21 | WiFiHelper.setup(); 22 | MpuHelper.setup(); 23 | 24 | resetReason = rtc_get_reset_reason(0); 25 | 26 | if (resetReason == POWERON_RESET) { 27 | pinMode(LED_BUILTIN, OUTPUT); 28 | 29 | WiFiHelper.server(); 30 | MpuHelper.server(); 31 | 32 | WebServerHelper.onSleep(deepSleep); 33 | WebServerHelper.start("cube"); 34 | } else if (!WiFiHelper.connect()) { 35 | deepSleep(); 36 | } 37 | 38 | sleepTime = millis() + 1000; 39 | } 40 | 41 | void loop() { 42 | MpuHelper.loop(); 43 | 44 | if (resetReason == POWERON_RESET) { 45 | digitalWrite(LED_BUILTIN, HIGH); 46 | delay(500); 47 | 48 | digitalWrite(LED_BUILTIN, LOW); 49 | delay(500); 50 | } else { 51 | if (MpuHelper.prevSide != MpuHelper.side) 52 | sleepTime = millis() + 1000; 53 | 54 | if (millis() >= sleepTime) 55 | deepSleep(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/display.cpp: -------------------------------------------------------------------------------- 1 | #include "WaveshareHelper.h" 2 | #include "WebServerHelper.h" 3 | #include "WifiHelper.h" 4 | #include "rom/rtc.h" 5 | 6 | RESET_REASON resetReason; 7 | 8 | void deepSleep() { 9 | WiFiHelper.sleep(); 10 | WaveshareHelper.sleep(); 11 | } 12 | 13 | void setup() { 14 | Serial.begin(115200); 15 | 16 | WiFiHelper.setup(); 17 | WaveshareHelper.setup(); 18 | 19 | resetReason = rtc_get_reset_reason(0); 20 | 21 | // Enter configuration mode on reset 22 | if (resetReason == POWERON_RESET) { 23 | pinMode(LED_BUILTIN, OUTPUT); 24 | 25 | WiFiHelper.server(); 26 | WaveshareHelper.server(); 27 | 28 | WebServerHelper.onSleep(deepSleep); 29 | WebServerHelper.start("display"); 30 | 31 | WaveshareHelper.update(false); 32 | return; 33 | } 34 | 35 | if (WiFiHelper.connect()) { 36 | WaveshareHelper.update(true); 37 | } 38 | 39 | deepSleep(); 40 | } 41 | 42 | void loop() { 43 | // Loop is only executed in configuration mode. 44 | digitalWrite(LED_BUILTIN, HIGH); 45 | delay(500); 46 | 47 | digitalWrite(LED_BUILTIN, LOW); 48 | delay(500); 49 | } 50 | -------------------------------------------------------------------------------- /src/knob.cpp: -------------------------------------------------------------------------------- 1 | #include "KnobHelper.h" 2 | #include "WebServerHelper.h" 3 | #include "WifiHelper.h" 4 | #include "rom/rtc.h" 5 | 6 | unsigned long sleepTime; 7 | RESET_REASON resetReason; 8 | 9 | void deepSleep() { 10 | WiFiHelper.sleep(); 11 | KnobHelper.sleep(); 12 | 13 | Serial.println("sleep"); 14 | esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 0); 15 | esp_deep_sleep_start(); 16 | } 17 | 18 | void setup() { 19 | Serial.begin(115200); 20 | 21 | WiFiHelper.setup(); 22 | KnobHelper.setup(); 23 | 24 | resetReason = rtc_get_reset_reason(0); 25 | 26 | if (resetReason == POWERON_RESET) { 27 | WiFiHelper.server(); 28 | KnobHelper.server(); 29 | 30 | WebServerHelper.onSleep(deepSleep); 31 | WebServerHelper.start("knob"); 32 | } else if (!WiFiHelper.connect()) { 33 | deepSleep(); 34 | } 35 | 36 | sleepTime = millis() + 2000; 37 | } 38 | 39 | void loop() { 40 | KnobHelper.loop(); 41 | delay(50); 42 | 43 | if (resetReason == POWERON_RESET) { 44 | digitalWrite(LED_BUILTIN, HIGH); 45 | delay(500); 46 | 47 | digitalWrite(LED_BUILTIN, LOW); 48 | delay(500); 49 | } else { 50 | if (KnobHelper.value != KnobHelper.prevValue) 51 | sleepTime = millis() + 2000; 52 | 53 | if (millis() >= sleepTime) 54 | deepSleep(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | --------------------------------------------------------------------------------