Jump to content
I2Cdevlib Forums

Jeff Rowberg

  • Posts

  • Joined

  • Last visited

  • Days Won


Everything posted by Jeff Rowberg

  1. In C++, you can specify default argument values in the prototype declaration. So you're on the right track, but the default values in this case are actually false. https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/MPU6050/MPU6050.h#L800
  2. Thanks for the heads-up. It's fixed now...it was a long-deprecated use of an array element mapping function, where the syntax that used to work now requires quotes. Simple enough, thankfully.
  3. It would be valuable to the community to have the library updated to MotionApps 5.1 (or 6.x?) based on what InvenSense has most recently published, but this is not a small endeavor. I am tied up with my main day job and a few other side projects at the moment, so it will either have to wait or someone else will have to do most of the development for now.
  4. I started the I2Cdevlib project in 2010 as a bit of yak-shaving intended to help with my Keyglove project, which at the time required access to nearly half a dozen I2C peripherals. It was a new protocol to me, but I saw value in creating an extensible device library rather than the independent and somewhat messy per-device implementations I’d seen. My happy little repository has grown to a reasonably well-known project with 4,868 forks as of the time of this post, thanks to the continuing interest of 58 contributors. I have the MPU-6050 IMU from InvenSense (now TDK) to credit for much of this interest, but others had an impact as well. However, I didn’t think ahead enough to set the project on a path that would cope with the level of interest that I2Cdevlib has since seen. I built the low-level interface code specifically on top of the Arduino Wire library, and specifically for register-based sensor devices. This has turned out to be severely limiting in certain cases, and to cause general headaches in many others. --- NOTE: THIS POST IS COPIED FROM THE ORIGINAL ON THE PERILIB DISCOURSE FORUM. THIS COPY IS LOCKED, BUT THE ORIGINAL IS NOT, AND I WELCOME COMMENTS ON IT OVER IN DISCOURSE. --- Shortcomings Laid Bare No doubt many developers who have used the I2Cdevlib code could point out numerous flaws in the project design. I’m here to concur wholeheartedly. Don’t get me wrong–there’s still a lot about the project that I like. But it would be a sad commentary on my evolving skillset if I thought everything I wrote eight years ago was still A-OK. Plus, there have been great improvements to GitHub during that time, and some fantastic new tools on the scene like Travis CI, which wasn’t around at all when I started. Here’s what I want to do better with this time. Fix #1: Repository Structure This is a big one. Although the Arduino IDE’s library manager wasn’t a thing in 2010, my choice to put everything into one repo has made it impossible to take advantage of it today. Actually, the original structure had only Arduino-specific code in it, without top-level folders for specific platforms. I decided to change this early in the project’s life, but instead of creating new repositories as I should have, I just moved stuff around and made the original repo more complicated. Oops. In addition to preventing integration with the almost any popular GitHub-aware IDE library manager available today, this also means every reported issue is in one giant bucket. There’s no logical separation for problems with the Arduino implementation of the MPU-6050 device code and the ESP32 implementation of the core I2Cdev hardware layer code. It’s a mess. This also means that anyone using the repo will necessarily have to pull down a huge amount of code that they will never use. Most people want the core hardware layer code and one or maybe two device libraries, only for the platform they’re using. Too bad! You have to download the entire fileset and move or copy only the pieces you need into your project or library folder. A monolithic structure also means there is no meaningful way to do commit-tagged versioning for official releases. The various sub-projects inside the repo are independent of each other, and it doesn’t make sense to call the entire thing “1.0.5” or whatever. The solution is to use a more flexible structure with a bunch of individual repositories for each logically separate component. For example: Arduino core hardware access layer Arduino MPU-6050 library Arduino ADXL345 library RasPi core hardware access layer RasPi MPU-6050 library RasPi ADXL345 library nRF52 core hardware access layer …and so on. These will also be under a GitHub organization account dedicated to this project, rather than my own personal account. It just makes more sense. Yes, we’ll end up with many more individual repos, but that’s not a problem. In any case, it’s a tiny price to pay for the benefits such a structure allows, including fixes for all of the problems mentioned above: Easy integration into library managers like the Arduino IDE and PlatformIO Per-library automated testing with Travis CI Per-library versioning and release tracking Per-library issue reporting Non-wasteful installation, no more cloning a majority of personally useless stuff Easy future expansion to new platforms simply by adding new repos It will also make it easier for specific contributors to “own” libraries that they commit or happen to be good at and interested in supporting. Trust me when I say nobody is going to fully grasp every aspect of every subproject on every platform in this kind of a repo, unless that’s their full-time job. But someone might be happy to assume responsibility of a library or two that they created and/or use regularly. Fix #2: Hardware Access Layer Code This is another big one. Using the Arduino Wire library (of 2010) as the foundation for everything else has made it practically impossible to add support for multiple I2C peripheral objects, timeouts, and some lower-level I2C functions. It also precludes the use of any other common peripheral protocols like SPI, UART, and 1-Wire, which is something I’ve wanted to do in some fashion for a very long time. For example, the SPI-supporting MPU-6000 sensor allows much faster data speeds than the I2C-only MPU-6050, while using virtually the same protocol. There’s no good reason the same library collection can’t support both. Further, the current architecture provides no easy way to independently choose which I2C implementation (Wire, NBWire, Fastwire, etc.) you want to use. You can switch by commenting or uncommenting certain #define statements in the I2Cdev core header, but this is unwieldy and non-obvious. All of the different implementations are bundled together in the same source files, which only gets worse as new implementations are added. The solution to both of these problems is to build reusable interface objects that are assigned to core and device instances when they are created. For example, on the Arduino Due platform, you could theoretically drive four MPU-6050 devices using the “Wire” and “Wire1” objects and both slave addresses on each set of two IMUs. You need only to be able to pass in the object you want to use. For example, here’s some pseudocode for it how might work for two Wire-based HAL instances powering four MPU-6050 IMUs: HalCore i2c0(&Wire); HalCore i2c1(&Wire1); MPU6050 imu0(&i2c0, 0x68); MPU6050 imu1(&i2c0, 0x69); MPU6050 imu2(&i2c1, 0x68); MPU6050 imu3(&i2c1, 0x69); With the correct class inheritance and abstraction, the top-layer “MPU6050” objects won’t either know or care what the underlying communication layer is. You could swap in NBWire instances for the “HalCore” objects just as easily. This implementation is not really more complicated than the original approach, but it has far more flexibility. Admittedly, supporting this on C-only platforms is not as straightforward due to the lack of classes and inheritance, but it isn’t impossible. Fix #3: Device Definitions The web-based database-driven register map and transaction analyzer are my favorite things about the original project, and they’re still online. The concept is great, but the implementation has turned out to be too inaccessible. Because of this, what could have been the most valuable aspect of this whole project has languished because I never quite managed to expose the creation/modification interface to the people most likely to do great things with it. To solve this problem and some related problems with version control of device definitions, I’ve decided to simply move everything into JSON files inside their own separate repo. Everything that was stored in the database can just as easily exist in this format, and this allows much greater transparency and the opportunity for easy new device contributions. It also provides a simple way for external projects to directly access device data for other purposes, and it makes it possible for Travis CI to automatically build code and documentation whenever a new device is created or an existing one is modified. The device definitions will be the heart of the whole project, responsible for a significant portion of code generation and required before any supporting repos, libraries, or examples can be submitted. As of the time of this post, I’m in the process of creating JSON schema definitions which will be used both for validation and for automatic form generation using the react-jsonschema-form plugin. This mechanism is clean, functional, and as easy as it possibly could be given the requirements. It also remains flexible if the schema gets new elements in the future, which will undoubtedly happen. The schema will be a work-in-progress for a while, but here are the known requirements so far: Unique device identification slugs Unique manufacturer identification slugs IC/module package and pin definitions for reference I2C address details (for I2C devices) Multiple protocol support (I2C, SPI, UART, etc.) Multiple simultaneous protocol variants (e.g. BNO080 IMU supports two different types of UART communication) Byte ordering definition at device level and register level Register-based protocol definitions (e.g. MPU-6050 IMU register map, structure, and multi-byte R/W behavior) Stream-based rigid binary protocol definitions (e.g. BLE112 module BGAPI protocol) Stream-based flexible text protocol definitions (e.g. WT12 module iWRAP protocol) Information about whether external pin signals are supported or required (e.g. interrupt, wake) My goal is to create the schema such that it can be extended to support new features when needed without breaking backwards compatibility. I know from experience that this is hard to do perfectly, but at least we’ve got nearly eight years of “wouldn’t-it-be-nice-if-we-could-only” experience. Fix #4: Hardware Analyzer Integration Here’s an area where a properly built peripheral library can shine, and where I always hoped to take I2Cdevlib. With an open and accessible collection of device and structure/protocol definitions, and a logic analyzer or even a passive sniffer like the Bus Pirate, it should be completely possible to have a simple Python GUI that spits out live, annotated analysis of peripheral transactions. Think Wireshark but for I2C/SPI/UART peripherals. This would not only be phenomenal for library development, but also great for troubleshooting. I’ll one-up myself here: with a cheap ATSAMD21-based MCU (not even Bus Pirate levels of financial expenditure) and a USB CDC interface, and a Chrome browser that supports the WebUSB specification, you could even run such a tool right in the browser. Note that WebUSB is supported in the Android version of Chrome. This means that you could have a powerful and portable protocol sniffer/analyzer with a cheap smartphone and $10 of external hardware running via USB OTG. (Yes, it might be easier to do the legwork with a real mobile app than trying to leverage Chrome, and I’m definitely open to that. There are some nice conveniences with a browser-only implementation though.) Think about this. With a bit of back-end data capture syncing, you could walk your smartphone and OTG analyzer over to a running device, grip RX, TX, and GND pins with spring clip probes, and just leave it there while you walk back to your desk and watch the data come in on the easier-to-work-with desktop version of the capture/analyzer website. I’m already salivating. But wait, there’s more! Reverse-engineering protocols is tedious. Even just translating a published specification from an API reference manual into source code (or JSON data, as the case may be) is mind-numbing work. The good news is that many protocols follow similar structural patterns, because there’s no reason to reinvent the wheel. With some intelligent guesswork, an analyzer tool could present some possible structural foundations to start from, and overlay them on top of a data capture for quick visual review. Start-of-frame bytes? Checksums? Type, length, or sequence bytes in a header? Pick the combination that seems to fit, then tweak the parameters until there are no misalignments. The front end of this would require some fancy programming in React (or similar), which admittedly I’m a bit rusty on, but the concept is not hard to grasp. I can see all of the moving pieces of this project working in my head. I know it’s technically possible. But it is a big project. Who’s with me?
  5. The baud rate in your serial monitor is incorrect; try 115200 instead, since this is what the DMP6 example uses: https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/MPU6050/examples/MPU6050_DMP6/MPU6050_DMP6.ino#L174
  6. Hello joh, The only reasoning behind the code contained in the dmpInitialize() method is that it imitates the behavior captured from the official InvenSense MPU-6050 evaluation board and ARM controller which I had access to back in 2011. The captured and analyzed data can be found here: http://www.i2cdevlib.com/tools/analyzer/1 I am not 100% certain whether every one of these I2C transactions is truly necessary.
  7. Hi Zapro, Sorry for the delayed reply. I moved the site to a new server a while ago and didn't correctly verify that the analyzer upload function still works. I have fixed the problems with upload permissions, and tested a new upload successfully just a moment ago. Also, there should be a "Report" link towards the bottom right of every post in the forum which allows you to report things for moderation. This link is next to the "Quote" button, and it is a very light faded gray unless your mouse is hovering over the post.
  8. Hello, This occurs due to gyro drift, particularly in the Z gyro axis since gravity can be used to compensate the others. The stabilization time after power-on is no doubt when using the raw measurements, but does not apply when using the DMP algorithm, which is far more complex. I have read that the DMP's internal auto-calibration routine requires 8 seconds of no motion in order to compensate for gyro drift. You can affect this by making use of the internal offset registers though, and this will improve both raw measurements and the DMP. I recommend checking out this post: http://www.i2cdevlib.com/forums/topic/91-how-to-decide-gyro-and-accelerometer-offsett/?hl=%2Bgyro+%2Bcalibration#entry257
  9. Removing the TWBR assignment in this case should still work fine, though I2C may operate at 100kHz. This line was added to switch the I2C clock to 400kHz, but it is specific to the ATmega chips. The ARM used on the Due does not have this register, and so the assignment fails.
  10. Hi Hans, I suspect the Serial.write() command is slowing you down. Can you double or quadruple the baud rate, or temporarily remove it and check the actual data capture rate by toggling a GPIO or something like that? The motion sensor should be able to read at 200Hz, but the Arduino itself or serial transfer might be the bottleneck. One other note is that I have heard that the 200Hz output rate is rather noisy compared to the 100Hz or lower rates.
  11. This code was contributed by another user, and I didn't have one of the actual sensors to test on my end. Have you figured out anything in the last month?
  12. Hmm, are you using the unmodified example sketch from the most recent updates on the repository? https://github.com/jrowberg/i2cdevlib/blob/master/Arduino/HMC5883L/Examples/HMC5883L_raw/HMC5883L_raw.ino Only the HMC5883L class is referenced there as far as I can tell.
  13. Hello @nicnac, While the MPU-6050 can act as its own I2C master and communicate with a slave such as an attached magnetometer, I am not sure how this is done specifically. I believe some people have attempted it with mixed results, but InvenSense hasn't been helpful in this regard: http://www.multiwii.com/forum/viewtopic.php?f=6&t=2040
  14. It sounds like your DMP is generating a different binary packet than what the existing MotionApps code is expecting, and since it sends larger packets and only reads smaller ones, eventually the FIFO overflows. You would need to somehow decode the DMP packet structure and change the read size to whatever the correct number of bytes is.
  15. Hi Wim, I don't have a very scientific calibration routine, or any code beyond what you saw posted there to speak of. The basic approach is like this: Use the set*Offset() methods to set all offsets to 0. These are effective immediately. Place the sensor on a flat, level surface. Measure with an actual level if you have to. The level adjustment is less critical if your main concern is yaw drift, but it's still a good baseline. Print out at least few dozen raw measurement values, or even better, skip 50 or so to let the device "settle" and then collect + calculate the average for each axis over 200+ readings. The correct "still/level" values for all gyro axes and the x/y accel axes should be 0, and the correct "still/level" value for the Z accel axis should be 1g, or +16384 at the default +/- 2g sensitivity setting. Use the opposite (positive/negative) value of the calculated averages to determine the new offset values for each axis. Plug the new values in on start-up using the set*Offset() methods. Note that there is some scale factor which I am not sure of yet; I believe the offset values need to be smaller, possibly by a factor of 2x, 4x, or 8x from the raw measurements. This is about all the info I currently have. I really should build an auto-calibration routine to do this, but I haven't had the time.
  16. Hi Szan, This would be quite a challenge, due primarily to the small code space available on the ATTiny, and partly due to the DMP requirements. Basically, you have to power on the MPU chip, then load a ~2 kByte block of binary data in over I2C in order to "prime" the DMP so that it can start generating quaternions. This data has to come from somewhere, and on the Arduino port, it is stored in the flash space. This would probably not be possible on the ATTiny85, since you'd run out of space too quickly. It is probably not impossible if you are very clever, but it would be difficult at best.
  17. You're very welcome! Thanks for posting just to say that. :-) Good luck with your UAV and the design competition.
  18. Hi KBill, I don't think anyone has done that yet. You would be the first, to the best of my knowledge!
  19. Joe, that's pretty impressive stabilization! Thanks for sharing the video.
  20. Hi Chandru, Have you tried alternative addresses for the AK8975 slave, such as 0x0D or 0x0E? Also, have you enabled I2C slave bypass mode on the MPU9150? The AK8975 inside the MPU is wired up to the AUX I2C pins rather than the main I2C pins, so you will not be able to talk directly to the magnetometer without first enabling bypass. I have not yet worked with the MPU9150 much yet, so I am kind of guessing here.
  21. Hi Marc, The REALACCEL numbers are calculated with respect to the orientation of the sensor itself, so that if it is flat and you move it straight up, the "Z" accel will change, but if you flip it up on one side and move it in the new relative "up" direction (along the sensor's Z axis), it will still register acceleration on the Z axis. Essentially, it is sensor-oriented acceleration which removes the effects of gravity and any non-flat/level orientation. The WORLDACCEL numbers are calculated to ignore orientation. Moving it straight up while flat will look the same as the REALACCEL numbers, but if you then flip it upside-down and do the exact same movement ("up" with respect to you), you'll get exactly the same numbers as before, even though the sensor itself is upside-down. This would be easier to explain in person with a solid object for reference, but hopefully this is enough.
  22. To my knowledge, if you change the sensitivity settings (either accel or gyro) after the DMP has been initialized, it will stop working as intended. Without knowledge of the DMP's internals, I imagine it isn't possible to get around this.
  23. Hi Marc, Unfortunately this is an as-yet-incomplete device entry in the I2Cdevlib database. The device shouldn't be shown, but the code isn't smart enough. The device doesn't have a complete register map, so the code can't be auto-generated. Sorry about that. :-(
  24. Hi Joachim, You could try to port the Wire library, but that would probably be more headache than it's worth. (BTW, the Arduino build/library system is a very bad way to get a feel for C++, since the structure is meant to be easy and apparently tailored for very small projects.) I wrote the device classes to only ever use the I2Cdev static class methods, and then wrote the I2Cdev class to reuse its own code as much as possible and only touch the hardware in four very specific functions: I2Cdev::readBytes() I2Cdev::readWords() I2Cdev::writeBytes() I2Cdev::writeWords() The actual TWI hardware interfacing is done in those four functions alone. The Bytes vs. Words methods are almost identical except for the way they treat the stored data. I would recommend cutting out the platform-specific code in those functions, then adding in PIC32MX-specific TWI calls. This will be the fastest path to a new port.
  25. Hi Jan, The content of that source file was obtained through reverse-engineering, and for my part I put it under the MIT license, so no permission is necessary as far as I am aware.
  • Create New...