Teensy I2C Sniffer for MPU6050

Status
Not open for further replies.

paynterf

Well-known member
I believe I have finally succeeded in creating a usable I2C sniffer, somewhat specialized for monitoring traffic between an I2C master and a MPU6050 slave.

The complete sniffer program is available from my Github account here. The complete Arduino Mega program I used to test the sniffer program is given below.

Thanks to everyone who contributed their suggestions and ideas for this project; I had a lot of fun and learned a lot about I2C, Teensy programming and ISRs.

For all the gory details, see my 'Paynters Palace' blog post here.

Frank
 
Very cool looking. I don't have an MPU6050 to watch - wondering what happens if it just watches any random i2C transfers?

Looks like this external file is really needed - not on my system:
>> #include "helper_3dmath.h" //Arduino\Libraries\i2cdevlib\Arduino\MPU6050\ needed to compute yaw from MPU6050 DMP packet
T:\tCode\i2c\Teensy_I2C_Sniffer_V11-master\Teensy_I2C_Sniffer_V11\Teensy_I2C_Sniffer_V11.ino:37:120: fatal error: helper_3dmath.h: No such file or directory
compilation terminated.
www gives me that from :: https://github.com/ElectronicCats/mpu6050/blob/master/src/helper_3dmath.h

> ignores Silly #pragma warnings like in IDE based build from SublimeText editor: #pragma region PROCESSING_VARIABLES
> fails build on T_4.0 because of GPIO access:
-- > T:\tCode\i2c\Teensy_I2C_Sniffer_V11-master\Teensy_I2C_Sniffer_V11\Teensy_I2C_Sniffer_V11.ino:139:21: error: 'GPIOB_PDIR' was not declared in this scope
-- > current_portb = GPIOB_PDIR & 12; //reads state of SDA (18) & SCL (19) at same time
> builds for T_3.6 and seems ready to run:
Serial available after 1500 mSec
2001: Waiting for Data...
2201: Waiting for Data...
2401: Waiting for Data...

Not sure why but the T_3.6 had some false starts - after starting as above - then re-uploading code and it is running and running ...

And hooking it up to a T4 running an i2c SSD1306 at 400 MHz:
Code:
485542 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
485542 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
485542 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
485542 I2C(3c) writing 1 bytes to 40... 0 . Done
485542 I2C(3c) writing 5 bytes to 0... 22 0 ff 21 0 . Done
485542 I2C(3c) writing 1 bytes to 0... 7f . Done
485541: processed = 2048 elements in 1 mSec

485555 I2C(3c) writing 31 bytes to 40... 49 49 49 49 49 49 49 49 49 29 29 29 29 29 29 29 29 a5 a5 a5 a5 a5 a5 a5 a5 95 95 95 55 55 55 . Done
485555 I2C(3c) writing 31 bytes to 40... 55 55 55 55 55 55 d5 b5 b5 b5 ad ad ad ad 6d 6d 6d 6d 5b 5b 5b 5b 3b 3b 3b 3b 37 37 2f 2f 1f . Done
485555 I2C(3c) writing 31 bytes to 40... 1f 1f 1f 1f 1f 1f 1f f f f f f f f f 7 7 7 7 7 7 7 7 3 3 3 3 3 3 3 3 . Done
485555 I2C(3c) writing 11 bytes to 40... 1 1 1 1 11 11 11 11 91 91 89 . Done
485554: processed = 2048 elements in 1 mSec

485567 I2C(3c) writing 31 bytes to 40... 22 12 12 12 12 12 12 12 12 a a 9 9 9 9 9 9 5 5 5 5 4 4 4 4 2 2 2 2 2 2 . Done
485567 I2C(3c) writing 31 bytes to 40... 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
485567 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
485567 I2C(3c) writing 11 bytes to 40... 0 0 0 0 0 0 0 0 1 1 1 . Done
485566: processed = 2048 elements in 1 mSec

…


485651 I2C(3c) writing 1 bytes to 40... 0 . Done
485651 I2C(3c) writing 5 bytes to 0... 22 0 ff 21 0 . Done
485651 I2C(3c) writing 1 bytes to 0... 7f . Done
485651 I2C(3c) writing 31 bytes to 40... 11 11 11 11 11 11 11 11 91 91 91 91 91 91 91 91 89 89 89 89 89 89 89 89 49 49 49 49 49 49 49 . Done
485651 I2C(3c) writing 31 bytes to 40... 49 49 49 49 49 49 49 49 49 29 29 29 29 29 29 29 29 a5 a5 a5 a5 a5 a5 a5 a5 95 95 95 55 55 55 . Done
485651 I2C(3c) writing 31 bytes to 40... 55 55 55 55 55 55 d5 b5 b5 b5 ad ad ad ad 6d 6d 6d 6d db db db db bb bb 7b 7b 77 77 6f 6f 5f . Done
485650: processed = 2048 elements in 1 mSec

485664 I2C(3c) writing 31 bytes to 40... 1 1 1 1 11 11 11 11 91 91 89 89 88 88 88 88 48 48 48 48 44 44 44 44 24 24 24 24 24 24 24 . Done
485664 I2C(3c) writing 31 bytes to 40... 22 12 92 92 92 92 92 92 52 4a 4a 49 49 49 49 29 29 25 25 25 25 14 14 14 14 12 12 a a a a . Done
485664 I2C(3c) writing 31 bytes to 40... a a 9 5 5 5 5 5 5 3 2 2 2 2 2 2 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 . Done
485664 I2C(3c) writing 11 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 . Done
485664: processed = 2048 elements in 1 mSec


Not sure what this is telling me from the VS2019 here?:
1>------ Build started: Project: Teensy_I2C_Sniffer_V11, Configuration: Debug Win32 ------
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: The "VCMessage" task failed unexpectedly.
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: System.FormatException: Index (zero based) must be greater than or equal to zero and less than the size of the argument list.
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at System.String.Format(IFormatProvider provider, String format, Object[] args)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.Shared.ResourceUtilities.FormatString(String unformatted, Object[] args)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.Utilities.TaskLoggingHelper.FormatString(String unformatted, Object[] args)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.Utilities.TaskLoggingHelper.FormatResourceString(String resourceName, Object[] args)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorWithCodeFromResources(String messageResourceName, Object[] messageArgs)
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.CPPTasks.VCMessage.Execute()
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext()
1>Done building project "Teensy_I2C_Sniffer_V11.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Glad the FASTRUN helped!

the bDone is still there as it was? I see it runs continuously - maybe after filling one buffer toggle to a second buffer ? The it would never be Done - just run and toggle the buffer? Didn't look any deeper.
 
Defragster,

The only MPU6050-specific feature is the ability to recognized and decode the 28-byte DMP FIFO packet containing accelerometer data (this is the only thing the helper_3dmath.h library is used for). If you replace


Code:
void OutputFormattedSentence(int RW, uint8_t dev, uint8_t reg, uint8_t numbytes, uint8_t* bytearray, uint16_t startidx)
{
    Serial.printf("%lu I2C(%x) %s %d bytes %s %x... ",
        millis(), dev, (RW == 0 ? "writing" : "reading"),  numbytes - startidx, (RW == 0 ? "to" : "from"), reg);
    for (size_t i = startidx; i < numbytes; i++)
    {
        Serial.printf("%x ", bytearray[i]);
    }

    //01/18/20 experiment to decode 28-byte packet into yaw value
    if (numbytes == 28)
    {
        dmpGetQuaternion(&q, bytearray);
        dmpGetGravity(&gravity, &q);
        dmpGetYawPitchRoll(ypr, &q, &gravity);

        //compute the yaw value
        global_yawval = ypr[0] * 180 / M_PI;
        Serial.printf("yawval = %3.2f\n", global_yawval);
    }
    else
    {
        Serial.printf(". Done\n");
    }
}

with

Code:
void OutputFormattedSentence(int RW, uint8_t dev, uint8_t reg, uint8_t numbytes, uint8_t* bytearray, uint16_t startidx)
{
    Serial.printf("%lu I2C(%x) %s %d bytes %s %x... ",
        millis(), dev, (RW == 0 ? "writing" : "reading"),  numbytes - startidx, (RW == 0 ? "to" : "from"), reg);
    for (size_t i = startidx; i < numbytes; i++)
    {
        Serial.printf("%x ", bytearray[i]);
    }
	
	Serial.printf(". Done\n");
}

Then you can get rid of 'helper_3dmath.h' and all the support functions dealing with yaw value calculation (everything in #pragma region YAW_COMPUTATIONS). Then the code should work with any master/slave I2C setup, regardless of the slave device, with the following assumptions.

The code assumes a 'burst' of I2C data of 2048 bytes or fewer, followed by at least 2500 IDLE (0xc 0xc 0xc ...) bytes, followed by 1-2 mSec for processing/printing. If more than 2048 bytes are captured without IDLE detection, the program toggles the 'Done' flag anyway to avoid buffer overrun, at the cost of losing part of the I2C traffic. In my application, the bursts were typically 1200-1400 bytes over 5-10 mSec, followed by 195-190 mSec of IDLE. Processing takes just 1-2 mSec on a T3.2.

At one time I worked on a scheme that would load captured bytes into a circular buffer to avoid the above problem, but abandoned it when I couldn't get the circular buffer code to work, and I discovered that MPU6050 conversations in my application were 'bursts' of well less than 2048 bytes followed by long pauses (200 mSec in my case).

The program should run fine with larger buffers, up to whatever the processor can handle. The real limitation is the 1 uSec sample rate; you need at least two samples per baud, so it will probably do fine for 200Kbs I2C bus rate, and maybe even 400Kbs. If the T4 can sample faster, then higher I2C bus rates are possible.

The program is designed to run continuously. The original (and current) application is for long-term logging to troubleshoot intermittent failures where the I2C bus quits working for some unknown reason after minutes/hours/days. The idea is to analyze the sniffer logs up to the failure point and maybe determine what happened.

I use #pragma region to allow me to toggle blocks of code lines in/out, just for ease of reading. Add '-Wno-unknown-pragmas' to the 'extra cpp flags' line in Visual Micro->Compiler->Advanced to disable these warnings (see my thread in the VM forum about making that available as a global VM option).

No idea about the VS2019 error messages - compiles fine here on a T3.2. Here's the verbose compile output:

Code:
Compiling debug version of 'Teensy_I2C_Sniffer_V11' for 'Teensy 3.2 / 3.1'
Build Folder: "file:///C:/Users/Frank/AppData/Local/Temp/VMBuilds/Teensy_I2C_Sniffer_V11/teensy31/Debug"
Additional Defines: VM_DEBUGGER_TYPE_HARDWARESERIAL 0;VM_DEBUGGER_TYPE_SOFTWARESERIAL 1;VM_DEBUGGER_TYPE_FASTSERIAL 2;VM_DEBUGGER_TYPE_USB 3;VM_DEBUGGER_TYPE_TEENSY 4;VM_DEBUGGER_TYPE_UART 5;VM_DEBUGGER_TYPE_USART 6;VM_DEBUGGER_TYPE_USBSERIAL 7;VM_DEBUGGER_TYPE_TTYUART 8;VM_DEBUGGER_TYPE_NET_CONSOLE 9;VM_DEBUGGER_TYPE_Uart 10;VM_DEBUGGER_TYPE_COSA 11;VM_DEBUGGER_TYPE_CDCSerialClass 12;VM_DEBUGGER_TYPE_HARDWARESERIAL1 13;VM_DEBUGGER_TYPE_HARDWARESERIAL2 14;VM_DEBUGGER_TYPE_HARDWARESERIAL3 15;VM_DEBUGGER_TYPE_NET_UDP 16;VM_DEBUGGER_TYPE_USBAPI 17;VM_DEBUGGER_TYPE_SERIALUSB 18;VM_DEBUGGER_TYPE_MS430_SERIAL_ 19;VM_DEBUGGER_TYPE_NO_SERIAL 20;VM_DEBUGGER_TYPE_GENERIC_OBJECT 21;VM_DEBUG_ENABLE 1;VM_DEBUG;VM_DEBUG_BANDWIDTH_THROTTLE_MS 50;VM_DEBUGGER_SOFT_TRANSPORT Serial;VM_DEBUGGER_SOFT_TRANSPORT_WRITER Serial;VM_DEBUGGER_TYPE VM_DEBUGGER_TYPE_GENERIC_OBJECT;VM_DEBUG_BREAKPAUSE;
Architecture Tools: "file:///C:/Program%20Files%20(x86)/Arduino/hardware/tools/"
Api: 1.1911.23-1
Sketch Book: "file:///C:/Users/Frank/Documents/Arduino"
Sketch Include Paths
Core Include Paths
Include Path "file:///C:/Program%20Files%20(x86)/Arduino/hardware/teensy/avr/cores/teensy3"
 
Deep search for libraries ...
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne\utility" "C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne\TimerOne.cpp" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\utility" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_DBG.cpp" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\utility" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_mem_check.c" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\utility" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_mem_check_sam.cpp" -o "nul"
recipe.hooks.sketch.prebuild.1.pattern
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/precompile_helper" "C:\Program Files (x86)\Arduino\hardware\teensy\avr/cores/teensy3" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug" "C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -x c++-header -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\Program Files (x86)\Arduino\hardware\teensy\avr/cores/teensy3" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch/Arduino.h" -o "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch/Arduino.h.gch"
 
Building core ...
 
Using previously compiled file: C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\pch\Arduino.h.gch
Building libraries ...

Using library TimerOne version 1.1 in folder "file:///C:/Program%20Files%20(x86)/Arduino/hardware/teensy/avr/libraries/TimerOne"
  Using previously compiled file: C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\TimerOne\TimerOne.cpp.o
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -c -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant  -Wno-unknown-pragmas -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0 -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_DBG.cpp" -o "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\VM_DBG\VM_DBG.cpp.o"
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -c -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant  -Wno-unknown-pragmas -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0 -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_mem_check_sam.cpp" -o "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\VM_DBG\VM_mem_check_sam.cpp.o"
 
Building project code ...
  Using previously compiled file: C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp.o
 
Linking it all together ...
# Coping cached core C:\Users\Frank\AppData\Local\Temp\VMBCore\arduino16x\ab727a508ff6ae253f543daa72752d82\core.a to C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\core.a

"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-gcc" -O2 -Wl,--gc-sections,--relax,--defsym=__rtc_localtime=1579425138 "-TC:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3/mk20dx256.ld" -lstdc++ -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -o "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.elf" "Teensy_I2C_Sniffer_V11.cpp.o" "TimerOne\TimerOne.cpp.o" "VM_DBG\VM_DBG.cpp.o" "VM_DBG\VM_mem_check_sam.cpp.o" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/core.a" "-LC:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug" -larm_cortexM4l_math -lm
## recipe.objcopy.eep.pattern
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-objcopy" -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.elf" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.eep"
## recipe.objcopy.hex.pattern
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-objcopy" -O ihex -R .eeprom "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.elf" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.hex"

Program Teensy_I2C_Sniffer_V11 size: 38,928 bytes (used 15% of a 262,144 byte maximum) (3.08 secs)
Minimum Memory Usage: 11652 bytes (18% of a 65536 byte maximum)
 
recipe.hooks.postbuild.1.pattern
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/stdout_redirect" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.lst" "C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-objdump" -d -S -C "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.elf"
recipe.hooks.postbuild.2.pattern
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/stdout_redirect" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.sym" "C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-objdump" -t -C "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/Teensy_I2C_Sniffer_V11.ino.elf"
recipe.hooks.postbuild.3.pattern
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/teensy_post_compile" "-file=Teensy_I2C_Sniffer_V11.ino" "-path=C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug" "-tools=C:\Program Files (x86)\Arduino\hardware\teensy/../tools/" "-board=TEENSY31"
# Copy build result to 'Project>Property Pages>Intermediate Directory'
# Destination: file:///C:/Users/Frank/Documents/Arduino/Teensy_I2C_Sniffer_V11/Debug/




Frank
 
Anyone,

I have been trying to generalize my I2C sniffer for all I2C devices. Only the code that computes a yaw value from a 28-byte packet returned from a MPU6050 register is specific to the device; everything else should work fine with any I2C slave.

So, I placed #ifdef MPU6050_SPECIFIC/#endif guards around all these bits, expecting everything to compile fine, but of course it didn't :-(. The following code compiles fine.


Code:
/*
    Name:       Teensy_I2C_Sniffer_V11.ino
    Created:	1/18/2020 10:55:55 AM
    Author:     FRANKNEWXPS15\Frank
*/
/* 'Notes:

    A typical I2C sentence when communicating with a MPU6050 IMU module goes something like:
        "I2C(68) wrote 1 byte to 75 - C0 Done."
        "I2C(68) wrote 3 bytes to 72 - C0 0C 10 Done."
        "I2C(68) read 5 bytes from 6A - C0 0C 10 14 03 Done."

    To form a sentence, we need:
        Device addr: 68 in the above examples
        Read/Write direction
        To/From register address:  75, 72 and 6A in the above examples
        Data:  C0, C0 0C 10, and C0 0C 10 14 03 in the above examples
        number of bytes written/read:  1,3 & 5 in the above examples

     Each I2C communication proceeds as follows (assuming a START from an IDLE condition):
         A START or RESTART condition, denoted by SDA & SCL HIGH, followed by SDA LOW, SCL HIGH
         A 7-bit device address, MSB first (0x8/0xC = 1, 0x0/0x4 = 0)
         A R/W bit (0x8/0xC = read, 0x0/0x4 = write)
         An ACK bit (0x8/0xC = NAK, 0x0/0x4 = ACK)
         If the bus direction is WRITE, then
             A register address for read/write
             zero or more additional data bytes
         Else (the bus direction is READ)
            One or more additional data bytes
         Endif

    This version uses a fixed-size (2048 bytes) array instead of tonton81's circular buffer library.
*/
//#define MPU6050_SPECIFIC

#include <TimerOne.h> //needed for ISR

//#ifdef MPU6050_SPECIFIC
#include "helper_3dmath.h" //Arduino\Libraries\i2cdevlib\Arduino\MPU6050\ needed to compute yaw from MPU6050 DMP packet
//#endif // MPU6050_SPECIFIC

//#define PARSE_LOOP_DEBUG

const uint16_t CAPTURE_ARRAY_SIZE = 2048;
const uint16_t VALID_DATA_ARRAY_SIZE = 2048;
const int WAITING_PRINT_INTERVAL_MSEC = 200;//interval timer for 'Waiting for data...' printout

#define MONITOR_OUT1 2 //so can monitor ISR activity with O'scope
#define MONITOR_OUT2 3 //so can monitor ISR activity with O'scope
#define MONITOR_OUT3 4 //so can monitor ISR activity with O'scope
#define SDA_PIN 18 
#define SCL_PIN 19

#pragma region PROCESSING_VARIABLES
uint8_t devAddr;
uint8_t regAddr;
uint8_t databytes[2048]; //holds multiple databytes for later output sentence construction
uint16_t numbytes = 0; //number of data bytes extracted from data stream
int ACKNAKFlag; //can be negative
uint16_t databyte_idx = 0; //index into databyte_array
uint8_t killbuff[2]; //used to consume start/stop bytes
elapsedMillis mSecSinceLastWaitingPrint;
uint8_t valid_data[2048];
uint16_t numvalidbytes = 0; //number of valid bytes in this burst
uint16_t read_idx = 0; //pointer to next byte pair to be processed

//added for bus direction labels
enum BUSDIR
{
    WRITE,
    READ,
    UNKNOWN = -1
} RWDir;
BUSDIR BusDir = BUSDIR::UNKNOWN;
#pragma endregion ProcVars


#pragma region ISR_SUPPORT
uint8_t raw_data[CAPTURE_ARRAY_SIZE]; //holds data captured from I2C bus
volatile uint16_t  write_idx = 0;
volatile uint8_t   current_portb = 0xFF;
volatile uint8_t   last_portb = 0xFF;
volatile uint16_t mult0xCCount = 0;
const uint16_t MAX_IDLE_COUNT = 2500;

volatile bool bDone = false;
volatile bool bWaitingForStart = true;
volatile bool bIsData = true;
volatile bool bIsStart = false;
volatile bool bIsStop = false;
volatile uint8_t last_current;
#pragma endregion ISR Support

#ifdef MPU6050_SPECIFIC
Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
float global_yawval = 0; //updated by GetIMUHeadingDeg()
#endif // MPU6050_SPECIFIC

void setup()
{
    unsigned long now = millis();
    Serial.begin(1); //rate value ignored
    int idx = 0;
    while (!Serial && (millis() - now) < 3000)
    {
        delay(500);
        idx++;
    }
    Serial.printf("Serial available after %lu mSec\n", millis() - now);

    pinMode(MONITOR_OUT1, OUTPUT);
    digitalWrite(MONITOR_OUT1, LOW);
    pinMode(MONITOR_OUT2, OUTPUT);
    digitalWrite(MONITOR_OUT2, LOW);
    pinMode(MONITOR_OUT3, OUTPUT);
    digitalWrite(MONITOR_OUT3, LOW);

    pinMode(SCL_PIN, INPUT);
    pinMode(SDA_PIN, INPUT);

    //reset port byte vars & start timer
    last_portb = current_portb = 0;
    write_idx = 0;
    memset(raw_data, 255, CAPTURE_ARRAY_SIZE);
    //PrintNextArrayBytes(raw_data, 255, 20);
    Timer1.initialize(1); // run every mico second
    Timer1.attachInterrupt(capture_data);


    mSecSinceLastWaitingPrint = 0;
}
//-------------------------------------------------------------------------------
//--------------------------------    ISR    ------------------------------------
//-------------------------------------------------------------------------------
FASTRUN void capture_data()
//void capture_data()
{
    last_portb = current_portb;
    current_portb = GPIOB_PDIR & 12; //reads state of SDA (18) & SCL (19) at same time

    if (!bDone && last_portb != current_portb)
    {
        mult0xCCount = 0; //reset IDLE counter
        digitalWriteFast(MONITOR_OUT1, HIGH);

        //01/17/20: joepasquariello suggestion
        last_current = (last_portb << 4) | (current_portb);
        bIsStart = (last_current == 0xC4);
        bIsStop = (last_current == 0x4C);
        bIsData = (last_current == 0x04) || (last_current == 0x8C);

        if (bIsStart) //START  
        {
            digitalWriteFast(MONITOR_OUT2, HIGH);
            if (bWaitingForStart)
            {
                digitalWriteFast(MONITOR_OUT3, HIGH); //start of entire capture
                bWaitingForStart = false;
            }
        }
        else if (bIsStop) //STOP
        {
            digitalWriteFast(MONITOR_OUT2, LOW);
        }

        if (!bWaitingForStart && (bIsData || bIsStart || bIsStop))
        {
            //digitalWriteFast(MONITOR_OUT3, HIGH);
            raw_data[write_idx] = last_portb;
            write_idx++;
            raw_data[write_idx] = current_portb;
            write_idx++;
            if (write_idx >= CAPTURE_ARRAY_SIZE)
            {
                bDone = true;
                digitalWriteFast(MONITOR_OUT3, LOW);
            }
        }
        digitalWriteFast(MONITOR_OUT1, LOW);
    }
    else if (!bDone && mult0xCCount < MAX_IDLE_COUNT && last_portb == 0xc && current_portb == 0xc)
    {
        mult0xCCount++;
        if (mult0xCCount >= MAX_IDLE_COUNT)
        {
            digitalWriteFast(MONITOR_OUT3, LOW);
            bDone = true;
        }
    }
}
//-------------------------------------------------------------------------------
//-------------------------------- END ISR    ---------------------------------
//-------------------------------------------------------------------------------

void loop()
{
    if (bDone)
    { 
        if (write_idx > 14)
        {
            //OK, we have some data to process. IDLE detection must have been EOM
            Timer1.stop();

            unsigned long startMsec = millis();

            //Serial.printf("%lu\t %d\t", millis(), write_idx);
            //PrintNextArrayBytes(raw_data, 0, 50);
            //Serial.printf(" - %lu\n", millis());
            uint16_t numprocessed = DecodeAndPrintValidData(raw_data); //decode and print everything captured so far
            unsigned long endMsec = millis();
            Serial.printf("%lu: processed = %d elements in %lu mSec\n\n", startMsec, numprocessed, endMsec-startMsec);

            Timer1.start();
        }

        read_idx = 0;
        bDone = false;
        mult0xCCount = 0;
        write_idx = 0;
        bWaitingForStart = true;
    }    
    else
    {
        //no data to process, but don't blow prints out every mSec...
        if (mSecSinceLastWaitingPrint > WAITING_PRINT_INTERVAL_MSEC)
        {
            mSecSinceLastWaitingPrint -= WAITING_PRINT_INTERVAL_MSEC;
            Serial.printf("%lu: Waiting for Data...\n", millis());
        }
    }
}

void PrintNextArrayBytes(uint8_t* data, uint16_t startidx, uint16_t numbytes)
{
    Serial.printf("%d bytes starting at %d: ", numbytes, startidx);
    for (uint16_t i = 0; i < numbytes; i++)
    {
        Serial.printf("%x ", data[i + startidx]);
    }
}


uint16_t DecodeAndPrintValidData(byte* data)
{
    //Purpose:  decode and print I2C conversation held in raw_data array
    //Inputs:  
    //  cb = 2048 element FIFO
    //Outputs:
    //  returns number of bytes processed, or -1 for failure
    //  outputs structured I2C sentence to serial monitor
    //Plan:
    //  Step1: Cull out invalid bytes
    //  Step2: Determine if there is anything to do (have to have more than one transition in FIFO)
    //  Step3: Parse transitions into I2C sentence structure
    //  Step4: Output sentence to serial monitor

    memset(valid_data, 0, VALID_DATA_ARRAY_SIZE);
#ifdef PARSE_LOOP_DEBUG
    PrintNextArrayBytes(valid_data, 0, 20); //print out first 20 bytes for verification
#endif
    numvalidbytes = RemoveInvalidBytes(raw_data, valid_data);
#ifdef PARSE_LOOP_DEBUG
    Serial.printf("Removed %d invalid bytes, leaving %d remaining\n", write_idx + 1 - numvalidbytes, numvalidbytes);
    PrintNextArrayBytes(valid_data, 0, numvalidbytes); //print out first 20 bytes of valid_data array
#endif


    if (numvalidbytes < 2)
    {
        return 0;
    }

    while (read_idx < numvalidbytes)
    {
#ifdef PARSE_LOOP_DEBUG
        Serial.printf("At top of while (read_idx < numvalidbytes): read_idx = %d\n", read_idx);
        Serial.printf("Next two bytes in valid_data are %x, %x\n", valid_data[read_idx], valid_data[read_idx + 1]);
#endif
        //Find a START sequence (0xC followed by 0x4)
        while (!IsStart(valid_data, read_idx) && read_idx < numvalidbytes)
        {
            //Serial.printf("looking for start...\n");
            read_idx++;
        }
        //at this point, read_idx should point to next valid byte pair

#ifdef PARSE_LOOP_DEBUG
        Serial.printf("Start sequence found at %d\n", read_idx - 2);
        //PrintNextFIFOBytes(valid_data, 20);
#endif

        if (numvalidbytes - read_idx > 14)//14 entries required for 7-bit address
        {
            //Get 7-bit device address
            devAddr = Get7BitDeviceAddr(valid_data, read_idx);
#ifdef PARSE_LOOP_DEBUG
            Serial.printf("devAddr = %x\n", devAddr);
#endif
        }
        else
        {

#ifdef PARSE_LOOP_DEBUG
            Serial.printf("ran out of data at readidx = %d - exiting!\n", read_idx);
#endif
            break;
        }

        //get read/write flag  1 = Read, 0 = Write, -1 = error
        BusDir = (BUSDIR)GetReadWriteFlag(valid_data, read_idx);

#ifdef PARSE_LOOP_DEBUG
        Serial.printf("BusDir = %s\n", ((BusDir == BUSDIR::WRITE) ? "WRITE" : "READ"));
        //PrintNextFIFOBytes(valid_data, 20);
#endif

    //get ACK/NAK flag
        ACKNAKFlag = GetACKNAKFlag(valid_data, read_idx);
        numbytes = GetDataBytes(valid_data, read_idx, databytes); //terminates on a START, but the start bytes are not consumed
#ifdef PARSE_LOOP_DEBUG
        Serial.printf("Got %d bytes from GetDataBytes() --> ", numbytes);
        for (size_t i = 0; i < numbytes; i++)
        {
            Serial.printf(" %x ", databytes[i]);
        }
        Serial.printf("\n");

        //PrintNextFIFOBytes(cb_trans, 20);
#endif
    //If the bus direction is WRITE, then extract
    //    A register address for read / write
    //    zero or more additional data bytes
        if (BusDir == BUSDIR::WRITE)
        {
            regAddr = databytes[0];
#ifdef PARSE_LOOP_DEBUG
            Serial.printf("regAddr = %x, read_idx = %d\n", regAddr, read_idx);
#endif

            //check for additional data
            if (numbytes > 1)
            {
#ifdef PARSE_LOOP_DEBUG
                Serial.printf("Additional data found!\n");
                for (size_t i = 0; i < numbytes; i++)
                {
                    Serial.printf("data[%d] = %x\n", i, databytes[i]);
                }
#endif
                //1st byte is register addr, subsequent bytes are data
                OutputFormattedSentence(BusDir, devAddr, regAddr, numbytes, databytes, 1);
            }
        }
        else  //all bytes are data
        {
#ifdef PARSE_LOOP_DEBUG
            Serial.printf("In data block:  got %d bytes of data\n", numbytes);
            for (size_t i = 0; i < numbytes; i++)
            {
                Serial.printf("data[%d] = %x\n", i, databytes[i]);
            }
#endif
            OutputFormattedSentence(BusDir, devAddr, regAddr, numbytes, databytes, 0);
        }
#ifdef PARSE_LOOP_DEBUG
        Serial.printf("At end of while (read_idx < numvalidbytes): read_idx = %d\n", read_idx);
#endif

    }//while (read_idx < numvalidbytes)
    return numvalidbytes;
}


#pragma region Support Functions
bool IsStart(byte* data, uint16_t& readidx)
{
    bool result = false;

    //Serial.printf("IsStart[%d] = %x, IsStart[%d] = %x\n",
    //    readidx, data[readidx], readidx + 1, data[readidx + 1]);

    if (data[readidx] == 0xC && data[readidx + 1] == 0x4)
    {
        result = true;
        readidx += 2; //bump to next byte pair
    }
    return result;
}

bool IsStop(byte* data, uint16_t& readidx)
{
    bool result = false;

    //Serial.printf("IsStop[%d] = %x, IsStop[%d] = %x\n",
        //readidx, data[readidx], readidx + 1, data[readidx + 1]);

    if (data[readidx] == 0x4 && data[readidx + 1] == 0xC)
    {
        result = true;
        readidx += 2; //bump to next byte pair
    }
    return result;
}

uint8_t Get7BitDeviceAddr(byte* data, uint16_t& readidx)
{
    //Purpose: Construct a 7-bit address starting from dataidx
    //Inputs:
    //  data = pointer to valid data array
    //  readidx = starting index of 7-bit address sequence (MSB first)
    //Outputs:
    //  returns the address as an 8-bit value with the MSB = 0, or 0x0 if unsuccessful
    //  dataidx = pointer to next data entry
    //Plan:
    //  Step1: Convert a pair of data entries into a 0 or 1
    //  Step2: Add the appropriate value to an ongoing sum
    //  Step3: return the total.
    //Notes:
    //  A '0' is coded as a 0x0 followed by a 0x4
    //  A '1' is coded as a 0x8 followed by a 0xC

    uint8_t devAddr = 0x0; //failure return value

    //Serial.printf("Get7BitDeviceAddr: readidx = %d\n",readidx);

    //devAddr is exactly 7 bits long, so 8 bits with MSB = 0
    for (size_t i = 0; i < 7; i++)
    {
        if (data[readidx] == 0x0 && data[readidx + 1] == 0x4)
        {
            readidx += 2; //advance the pointer, but don't add to sum
        }

        else if (data[readidx] == 0x8 && data[readidx + 1] == 0xC)
        {
            //Serial.printf("Get7BitDeviceAddr: '1' found at i = %d, adding %x to devAddr to get %x\n",
            //    i, 1 << (7 - i), devAddr + (1 << (7-i)));

            readidx += 2; //advance the pointer
            devAddr += (1 << (7 - i)); //add 2^(7-i) to sum
        }
    }

    devAddr = devAddr >> 1; //divide result by 2 to get 7-bit addr from 8 bits
    return devAddr;
}

int Get8BitDataByte(byte* data, uint16_t& readidx)
{
    //Purpose: Construct a 8-bit data byte starting from dataidx
    //Inputs:
    //  data = pointer to valid data array
    //  readidx = starting index of 8-bit data byte (MSB first)
    //Outputs:
    //  returns the address as an 8-bit value, or 0x0 if unsuccessful
    //  dataidx = pointer to next data entry
    //Plan:
    //  Step1: Convert a pair of data entries into a 0 or 1
    //  Step2: Add the appropriate value to an ongoing sum
    //  Step3: return the total.
    //Notes:
    //  A '0' is coded as a 0x0 followed by a 0x4
    //  A '1' is coded as a 0x8 followed by a 0xC
    //  12/29/19 - changed return val to int, so can return -1 when a 'short byte' is detected

    int dataval = 0x0; //failure return value

#ifdef GET_8BIT_DATABYTE_DEBUG
    Serial.printf("Get8BitDataByte: data[%d] = %x, data[%d] = %x\n",
        readidx, data[readidx], readidx + 1, data[readidx + 1]);
#endif

    //8 bits with MSB = 0
    int numbytes = 0;
    for (size_t i = 0; i < 8; i++)
    {
        if (data[readidx] == 0x0 && data[readidx + 1] == 0x4)
        {
            readidx += 2; //advance the pointer, but don't add to sum
            numbytes++;
        }

        else if (data[readidx] == 0x8 && data[readidx + 1] == 0xC)
        {
#ifdef GET_8BIT_DATABYTE_DEBUG
            Serial.printf("Get8BitDataByte: '1' found at i = %d, adding %x to devAddr to get %x\n",
                i, 1 << (7 - i), dataval + (1 << (7 - i)));
#endif
            readidx += 2; //advance the pointer
            dataval += (1 << (7 - i)); //add 2^(8-i) to sum
            numbytes++;
        }
    }

#ifdef GET_8BIT_DATABYTE_DEBUG
    Serial.printf("Get8BitDataByte: numbytes = %d\n", numbytes);
#endif
    if (numbytes != 8)
    {
        dataval = -1; //error return value
    }

    return dataval;
}

int GetReadWriteFlag(byte* data, uint16_t& readidx)
{
    //Purpose: decode R/W byte pair
    //Inputs:
    //  data = pointer to valid data array
    //  readidx = index into data to start of R/W byte pair
    //Outputs:
    //  readidx = if successful, points to next byte pair in data
    //  returns 1 for Read (0x8/0xC), 0 for Write (0x0/0x4), -1 for failure
    //Notes:
    //  

    //Serial.printf("GetReadWriteFlag: readidx = %d, data[readidx] = %x, data[readidx+1]= %x\n",
    //    readidx, data[readidx], data[readidx + 1]);
    int result = 0;
    if (data[readidx] == 0x8 && data[readidx + 1] == 0xC)
    {
        result = 1; //read detected
        readidx += 2; //point to next byte pair
    }

    else if (data[readidx] == 0x0 && data[readidx + 1] == 0x4)
    {
        result = 0; //write detected
        readidx += 2; //point to next byte pair
    }
    else
    {
        result = -1; //failed to detect read or write
    }

    return result;
}

int GetACKNAKFlag(byte* data, uint16_t& readidx)
{
    //Purpose: decode ACK/NAK byte pair
    //Inputs:
    //  data = pointer to valid data array
    //  readidx = index into data to start of ACK/NAK byte pair
    //Outputs:
    //  readidx = if successful, points to next byte pair in data
    //  returns 1 for NAK (0x8/0xC), 0 for ACK (0x0/0x4), -1 for failure
    //Notes:
    //  

    //Serial.printf("GetACKNAKFlag: readidx = %d, data[readidx] = %x, data[readidx+1]= %x\n",
    //    readidx, data[readidx], data[readidx + 1]);
    int result = 0;
    if (data[readidx] == 0x8 && data[readidx + 1] == 0xC)
    {
        result = 1; //NAK detected
        readidx += 2; //point to next byte pair
    }

    else if (data[readidx] == 0x0 && data[readidx + 1] == 0x4)
    {
        result = 0; //ACK detected
        readidx += 2; //point to next byte pair
    }
    else
    {
        result = -1; //failed to detect ACK or NAK
    }

    return result;
}

int GetDataBytes(uint8_t* data, uint16_t& readidx, uint8_t* databytes)
{
    //Notes:
    //  01/01/2020: removed databyteidx from sig - always starts at zero

    uint16_t numbytes = 0;
    uint16_t databyte_idx = 0;

    bool StartFlag = false;
    bool StopFlag = false;

    do
    {
        int dataval = Get8BitDataByte(data, readidx);

        //watch out for 'short byte' reads
        if (dataval >= 0)
        {
            uint8_t databyte = (uint8_t)dataval;
            databytes[databyte_idx] = databyte;
            databyte_idx++;
            numbytes++;
        }

        ACKNAKFlag = GetACKNAKFlag(data, readidx);
        StartFlag = IsStart(data, readidx);
        StopFlag = IsStop(data, readidx);

#ifdef PARSE_LOOP_DEBUG
        Serial.printf("IsStart returned %d, IsStop returned %d, dataidx = %d\n",
            StartFlag, StopFlag, readidx);
#endif

    } while (!StartFlag && !StopFlag && readidx < numvalidbytes);


    readidx -= 2;//back readidx up so loop top is positioned correctly.

    return numbytes;
}

void OutputFormattedSentence(int RW, uint8_t dev, uint8_t reg, uint8_t numbytes, uint8_t* bytearray, uint16_t startidx)
{
    Serial.printf("%lu I2C(%x) %s %d bytes %s %x... ",
        millis(), dev, (RW == 0 ? "writing" : "reading"),  numbytes - startidx, (RW == 0 ? "to" : "from"), reg);
    for (size_t i = startidx; i < numbytes; i++)
    {
        Serial.printf("%x ", bytearray[i]);
    }

#ifdef MPU6050_SPECIFIC


    //01/18/20 experiment to decode 28-byte packet into yaw value
    if (numbytes == 28)
    {
        dmpGetQuaternion(&q, bytearray);
        dmpGetGravity(&gravity, &q);
        dmpGetYawPitchRoll(ypr, &q, &gravity);

        //compute the yaw value
        global_yawval = ypr[0] * 180 / M_PI;
        Serial.printf("yawval = %3.2f\n", global_yawval);
    }
    else
    {
        Serial.printf(". Done\n");
    }
#endif // MPU6050_SPECIFIC



}

uint16_t RemoveInvalidBytes(uint8_t* rawdata, uint8_t* validdata)
{
    uint16_t numvalid = 0;
    uint16_t valididx = 0;

    //Serial.printf("raw data array contains %d bytes\n", write_idx + 1);
    //PrintNextArrayBytes(raw_data, 0, 20);

    //OK, now go back through the array, excising invalid sequences
    for (uint16_t rawidx = 0; rawidx < write_idx;/*rawidx incremented internally*/)
    {
        uint8_t firstByte = raw_data[rawidx]; //get the first byte
        uint8_t secondByte = raw_data[rawidx + 1]; //get the next byte
        bool validpair =
            (
            (firstByte == 0xC && secondByte == 0x4) //START or RESTART
                || (firstByte == 0x4 && secondByte == 0xC) //STOP
                || (firstByte == 0x0 && secondByte == 0x4) //0 OR ACK
                || (firstByte == 0x8 && secondByte == 0xC) //1 or NAK
                );

        //Serial.printf("rawidx %d: Considering %x and %x: validity = %d\n",
            //rawidx, firstByte, secondByte, validpair);
        if (validpair)
        {
            //save valid bytes to valid_bytes array
            validdata[valididx] = firstByte;
            validdata[valididx + 1] = secondByte;
            numvalid += 2;
            //Serial.printf("Added %x & %x at idx = %d & %d\n", firstByte, secondByte, valididx, valididx + 1);
            //PrintNextArrayBytes(validdata,0,numvalid);
            rawidx += 2;
            valididx += 2;
        }
        else
        {
            rawidx++; //on invalid, just go to next byte
        }
    }

    return numvalid;
}
#pragma endregion Support Functions

#ifdef MPU6050_SPECIFIC

#pragma region YAW_COMPUTATIONS
//01/18/2020: I copied these functions from MPU6050_6Axis_MotionApps_V6_12.h and
//  modified them to be called directly instead of from an 'mpu' object
    
uint8_t dmpGetQuaternion(int16_t* data, const uint8_t* packet) 
{
    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
    if (packet != 0) 
    {
        data[0] = ((packet[0] << 8) | packet[1]);
        data[1] = ((packet[4] << 8) | packet[5]);
        data[2] = ((packet[8] << 8) | packet[9]);
        data[3] = ((packet[12] << 8) | packet[13]);
    }
    return 0;
}

uint8_t dmpGetQuaternion(Quaternion* q, const uint8_t* packet) 
{
    // TODO: accommodate different arrangements of sent data (ONLY default supported now)
    int16_t qI[4]{ 0,0,0,0 };
    uint8_t status = dmpGetQuaternion(qI, packet);
    if (status == 0) {
        q->w = (float)qI[0] / 16384.0f;
        q->x = (float)qI[1] / 16384.0f;
        q->y = (float)qI[2] / 16384.0f;
        q->z = (float)qI[3] / 16384.0f;
        return 0;
    }
    return status; // int16 return value, indicates error if this line is reached
}

uint8_t dmpGetYawPitchRoll(float* data, Quaternion* q, VectorFloat* gravity) {
    // yaw: (about Z axis)
    data[0] = atan2(2 * q->x * q->y - 2 * q->w * q->z, 2 * q->w * q->w + 2 * q->x * q->x - 1);
    // pitch: (nose up/down, about Y axis)
    data[1] = atan2(gravity->x, sqrt(gravity->y * gravity->y + gravity->z * gravity->z));
    // roll: (tilt left/right, about X axis)
    data[2] = atan2(gravity->y, gravity->z);
    if (gravity->z < 0) {
        if (data[1] > 0) {
            data[1] = PI - data[1];
        }
        else {
            data[1] = -PI - data[1];
        }
    }
    return 0;
}


uint8_t dmpGetGravity(VectorFloat* v, Quaternion* q) {
    v->x = 2 * (q->x * q->z - q->w * q->y);
    v->y = 2 * (q->w * q->x + q->y * q->z);
    v->z = q->w * q->w - q->x * q->x - q->y * q->y + q->z * q->z;
    return 0;
}
#pragma endregion YAW_COMPUTATIONS
#endif //MPU6050_SPECIFIC

All of the MPU6050 code *except* the '#include #include "helper_3dmath.h" ' has been #ifdef'd out. However, if I then UNcomment the #ifdef surrounding that one remaining MPU6050-specific line, then the code no longer compiles, and instead blows a bunch of errors, as shown below in the verbose compile output.

Code:
Compiling debug version of 'Teensy_I2C_Sniffer_V11' for 'Teensy 3.2 / 3.1'
Build Folder: "file:///C:/Users/Frank/AppData/Local/Temp/VMBuilds/Teensy_I2C_Sniffer_V11/teensy31/Debug"
Additional Defines: VM_DEBUGGER_TYPE_HARDWARESERIAL 0;VM_DEBUGGER_TYPE_SOFTWARESERIAL 1;VM_DEBUGGER_TYPE_FASTSERIAL 2;VM_DEBUGGER_TYPE_USB 3;VM_DEBUGGER_TYPE_TEENSY 4;VM_DEBUGGER_TYPE_UART 5;VM_DEBUGGER_TYPE_USART 6;VM_DEBUGGER_TYPE_USBSERIAL 7;VM_DEBUGGER_TYPE_TTYUART 8;VM_DEBUGGER_TYPE_NET_CONSOLE 9;VM_DEBUGGER_TYPE_Uart 10;VM_DEBUGGER_TYPE_COSA 11;VM_DEBUGGER_TYPE_CDCSerialClass 12;VM_DEBUGGER_TYPE_HARDWARESERIAL1 13;VM_DEBUGGER_TYPE_HARDWARESERIAL2 14;VM_DEBUGGER_TYPE_HARDWARESERIAL3 15;VM_DEBUGGER_TYPE_NET_UDP 16;VM_DEBUGGER_TYPE_USBAPI 17;VM_DEBUGGER_TYPE_SERIALUSB 18;VM_DEBUGGER_TYPE_MS430_SERIAL_ 19;VM_DEBUGGER_TYPE_NO_SERIAL 20;VM_DEBUGGER_TYPE_GENERIC_OBJECT 21;VM_DEBUG_ENABLE 1;VM_DEBUG;VM_DEBUG_BANDWIDTH_THROTTLE_MS 50;VM_DEBUGGER_SOFT_TRANSPORT Serial;VM_DEBUGGER_SOFT_TRANSPORT_WRITER Serial;VM_DEBUGGER_TYPE VM_DEBUGGER_TYPE_GENERIC_OBJECT;VM_DEBUG_BREAKPAUSE;
Architecture Tools: "file:///C:/Program%20Files%20(x86)/Arduino/hardware/tools/"
Api: 1.1911.23-1
Sketch Book: "file:///C:/Users/Frank/Documents/Arduino"
Sketch Include Paths
Core Include Paths
Include Path "file:///C:/Program%20Files%20(x86)/Arduino/hardware/teensy/avr/cores/teensy3"
 
Deep search for libraries ...
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp" -o "nul"
Using previous search results: C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne\TimerOne.cpp
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\utility" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_DBG.cpp" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\utility" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_mem_check.c" -o "nul"
"C:\Program Files (x86)\Arduino\hardware\teensy\..\tools\arm\bin\arm-none-eabi-g++" -E -CC -x c++ -w  -g -Wall -ffunction-sections -fdata-sections -nostdlib -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0  -Wno-unknown-pragmas   -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\utility" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_mem_check_sam.cpp" -o "nul"
recipe.hooks.sketch.prebuild.1.pattern
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/precompile_helper" "C:\Program Files (x86)\Arduino\hardware\teensy\avr/cores/teensy3" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug" "C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -x c++-header -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\Program Files (x86)\Arduino\hardware\teensy\avr/cores/teensy3" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch/Arduino.h" -o "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch/Arduino.h.gch"
 
Using previously compiled file: C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\pch\Arduino.h.gch
Building core ...
 
Building libraries ...

Using library TimerOne version 1.1 in folder "file:///C:/Program%20Files%20(x86)/Arduino/hardware/teensy/avr/libraries/TimerOne"
  Using previously compiled file: C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\TimerOne\TimerOne.cpp.o
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -c -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant  -Wno-unknown-pragmas -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0 -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_DBG.cpp" -o "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\VM_DBG\VM_DBG.cpp.o"
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -c -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant  -Wno-unknown-pragmas -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0 -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" "c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG\VM_mem_check_sam.cpp" -o "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\VM_DBG\VM_mem_check_sam.cpp.o"
 
Building project code ...
"C:\Program Files (x86)\Arduino\hardware\teensy/../tools/arm/bin/arm-none-eabi-g++" -c -O2 -g -Wall -ffunction-sections -fdata-sections -nostdlib -MMD -fno-exceptions -fpermissive -felide-constructors -std=gnu++14 -Wno-error=narrowing -fno-rtti -mthumb -mcpu=cortex-m4 -fsingle-precision-constant  -Wno-unknown-pragmas -DVM_DEBUG_BREAKPAUSE -DVM_DEBUGGER_TYPE=VM_DEBUGGER_TYPE_GENERIC_OBJECT -DVM_DEBUGGER_SOFT_TRANSPORT_WRITER=Serial -DVM_DEBUGGER_SOFT_TRANSPORT=Serial -DVM_DEBUG_BANDWIDTH_THROTTLE_MS=50 -DVM_DEBUG -DVM_DEBUG_ENABLE=1 -DVM_DEBUGGER_TYPE_GENERIC_OBJECT=21 -DVM_DEBUGGER_TYPE_NO_SERIAL=20 -DVM_DEBUGGER_TYPE_MS430_SERIAL_=19 -DVM_DEBUGGER_TYPE_SERIALUSB=18 -DVM_DEBUGGER_TYPE_USBAPI=17 -DVM_DEBUGGER_TYPE_NET_UDP=16 -DVM_DEBUGGER_TYPE_HARDWARESERIAL3=15 -DVM_DEBUGGER_TYPE_HARDWARESERIAL2=14 -DVM_DEBUGGER_TYPE_HARDWARESERIAL1=13 -DVM_DEBUGGER_TYPE_CDCSerialClass=12 -DVM_DEBUGGER_TYPE_COSA=11 -DVM_DEBUGGER_TYPE_Uart=10 -DVM_DEBUGGER_TYPE_NET_CONSOLE=9 -DVM_DEBUGGER_TYPE_TTYUART=8 -DVM_DEBUGGER_TYPE_USBSERIAL=7 -DVM_DEBUGGER_TYPE_USART=6 -DVM_DEBUGGER_TYPE_UART=5 -DVM_DEBUGGER_TYPE_TEENSY=4 -DVM_DEBUGGER_TYPE_USB=3 -DVM_DEBUGGER_TYPE_FASTSERIAL=2 -DVM_DEBUGGER_TYPE_SOFTWARESERIAL=1 -DVM_DEBUGGER_TYPE_HARDWARESERIAL=0 -D__MK20DX256__ -DTEENSYDUINO=148 -DARDUINO=108010 -DF_CPU=96000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH "-IC:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug/pch" -I"C:\Users\Frank\Documents\Arduino\Teensy_I2C_Sniffer_V11" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\cores\teensy3" -I"C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries\TimerOne" -I"c:\program files (x86)\microsoft visual studio\2019\community\common7\ide\extensions\t4fb1en2.4xb\Micro Platforms\default\debuggers\VM_DBG" "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp" -o "C:\Users\Frank\AppData\Local\Temp\VMBuilds\Teensy_I2C_Sniffer_V11\teensy31\Debug\Teensy_I2C_Sniffer_V11.cpp.o"
 
Teensy_I2C_Sniffer_V11.ino: 63:26: error: 'uint8_t dmpGetQuaternion' redeclared as different kind of symbol
   elapsedMillis mSecSinceLastWaitingPrint
Teensy_I2C_Sniffer_V11.ino:62: note  previous declaration uint8_t dmpGetQuaternion(int16_t*, const uint8_t*)
   uint8_t killbuff[2]; \\used to consume start\stop bytes
 
Teensy_I2C_Sniffer_V11.ino: 63:26: error: 'Quaternion' was not declared in this scope
Error compiling project sources
   elapsedMillis mSecSinceLastWaitingPrint
 
Teensy_I2C_Sniffer_V11.ino: 63:38: error: 'q' was not declared in this scope
   elapsedMillis mSecSinceLastWaitingPrint
 
Teensy_I2C_Sniffer_V11.ino: 63:41: error: expected primary-expression before 'const
 
Teensy_I2C_Sniffer_V11.ino: 64:41: error: 'Quaternion' has not been declared
 
Teensy_I2C_Sniffer_V11.ino: 64:56: error: 'VectorFloat' has not been declared
 
Teensy_I2C_Sniffer_V11.ino: 65:23: error: 'VectorFloat' was not declared in this scope
   uint16_t numvalidbytes = 0; \\number of valid bytes in this burst
 
Teensy_I2C_Sniffer_V11.ino: 65:36: error: 'v' was not declared in this scope
   uint16_t numvalidbytes = 0; \\number of valid bytes in this burst
 
Teensy_I2C_Sniffer_V11.ino: 65:39: error: 'Quaternion' was not declared in this scope
   uint16_t numvalidbytes = 0; \\number of valid bytes in this burst
 
Teensy_I2C_Sniffer_V11.ino: 65:51: error: 'q' was not declared in this scope
   uint16_t numvalidbytes = 0; \\number of valid bytes in this burst
Teensy_I2C_Sniffer_V11.ino: 65:52: warning: expression list treated as compound expression in initializer [-fpermissive]
   uint16_t numvalidbytes = 0; \\number of valid bytes in this burst
Debug build failed for project 'Teensy_I2C_Sniffer_V11'

Anyone have any idea why this is happening?

TIA,

Frank
 
Never mind. I found this post that explains the problem. Apparently the Arduino pre-processor 'sees' the #include line before it 'sees' the #ifdef/#endif block - bummer.

So, to generalize the code I'll use #define MPU6050_SPECIFIC and #ifdef/#endif blocks to exclude all the necessary inline code blocks, but the user will have to manually comment out the #include "helper_3dmath.h" line to complete the task. I'll try and get this up on my Github site soon.

Frank
 
For whatever it is worth I was able to compile it for Arduino 3.2 after I uncommented the #if/#endif around the include of the math library at the top.

Arduino does give me lots of warnings like: bar:379: warning: ignoring #pragma region Support

Note it will not compile for T4 as it is using T3.x specific register names...
 
All,

I have updated the Teensy sniffer on my Github account to allow its use with any I2C slave device via a #ifdef MPU6050_SPECIFIC pre-processor switch. By default this switch is disabled, so the code will operate with any I2C slave device.

To specialize it for the MPU6050 simply uncomment the line

//#define MPU6050_SPECIFIC

and recompile.

The 'ignoring unknown pragma' warning can be silenced by adding '-Wno-unknown-pragmas' to the 'extra cpp flags'. If you are using Visual Studio and Visual Micro this is accomplished with vMicro->Compiler->Advanced. I like using this feature to easily collapse large sections of code.

I would entertain a pull request to allow the code to be used on a T4, especially if someone wants to send me one for testing ;-)))

Frank
 
Running it against i2c SSD1306 OLED at 100 MHz and it looks to be catching data - mostly works if bumped to 400 MHz - but that will not sample at right rate and enough at 1 us - and it hangs after some cycles … running on a T_3.6 at 180 or 256 MHz.

Nice work. Would be good to have forum and blog links on the github.
 
I did a quick and dirty update to build on T4... I may do a PR, but I did a Arduino code format on my version and it

It is up at: https://github.com/KurtE/Teensy_I2C_Sniffer_V11/tree/T4

I did it pretty quick and dirty:
Code:
FASTRUN void capture_data()
//void capture_data()
{
  last_portb = current_portb;
#if defined(__IMXRT1062__)
  current_portb = (digitalReadFast(SCL_PIN) << 2) | (digitalReadFast(SDA_PIN) << 3);
#else
  current_portb = GPIOB_PDIR & 12; //reads state of SDA (18) & SCL (19) at same time
#endif
 
I did a quick and dirty update to build on T4... I may do a PR, but I did a Arduino code format on my version and it
...
I did it pretty quick and dirty:
Code:
FASTRUN void capture_data()
//void capture_data()
{
  last_portb = current_portb;
#if defined(__IMXRT1062__)
  current_portb = (digitalReadFast(SCL_PIN) << 2) | (digitalReadFast(SDA_PIN) << 3);
#else
  current_portb = GPIOB_PDIR & 12; //reads state of SDA (18) & SCL (19) at same time
#endif

Put this in copy here and on T4 versus T_3.6 it is missing many of the i2c address values as '3c' to the SSD1306. Showing as : 28,6f,51,3b,24,a,73,3f,0,other. There were some few odd #'s using T_3.6 - but they were rare and usually hit the same non-3c value.
 
Is this somehow right - with the bits in the right order and location?

Code:
    /*
    #define CORE_PIN18_PORTREG  GPIO6_DR
    #define CORE_PIN19_PORTREG  GPIO6_DR

    #define CORE_PIN18_BIT      17  // SDA
    #define CORE_PIN19_BIT      16  // SCL

                } else if (pin == 18) {
                    CORE_PIN18_PORTSET = CORE_PIN18_BITMASK;
                } else if (pin == 19) {
                    CORE_PIN19_PORTSET = CORE_PIN19_BITMASK;
    */

[B]#if defined(__IMXRT1062__)
#define IMXRT_GPIO6_DIRECT  (*(volatile uint32_t *)0x42000000)
#define IO_SCL_SDA ((IMXRT_GPIO6_DIRECT & (CORE_PIN18_BITMASK | CORE_PIN19_BITMASK)) >> 14)
    current_portb = IO_SCL_SDA;[/B]

Majority showing "I2C(3c)":
Code:
157116 I2C(3c) writing 31 bytes to 40... aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa . Done
157116 I2C(3c) writing 31 bytes to 40... aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa aa . Done
157116 I2C(3c) writing 31 bytes to 40... aa aa aa aa aa aa aa aa aa aa aa aa aa ab a8 af a0 bf 80 ff 0 ff 0 ff 0 ff 0 ff ff 80 bf . Done
157116 I2C(3c) writing 11 bytes to 40... a0 af a8 ab aa aa aa aa aa aa aa . Done
157116: processed = 2048 elements in 0 mSec

<edit> : hacked from Reading-multiple-GPIO-pins-on-the-Teensy-4-0-"atomically"

Should be written to use : #define IMXRT_GPIO6 (*(IMXRT_REGISTER32_t *)0x42000000)
 
Last edited:
Hi @defragster - I should warn that I have not given this a ton of thought. I was simply trying to emulate getting the two values for SCL/SDA into the same bits...

A safer way maybe not to assume 0 and 1... So I have also now have:
Code:
  current_portb = (digitalReadFast(SCL_PIN)? 4 : 0) | (digitalReadFast(SDA_PIN)? 8 : 0);

This is updating the SSD1306 that has different address than Adafruit default:
Code:
18681: processed = 2048 elements in 0 mSec

18685 I2C(38) writing 30 bytes to 80... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 c 1d 3d 7d d9 e6 ef 2d ef ef ef c9 . Done
18685 I2C(38) writing 30 bytes to 80... 30 30 30 0 0 0 0 0 0 8 8 0 0 0 0 0 8 10 30 0 0 0 0 0 0 0 0 0 0 0 . Done
18685 I2C(3c) writing 13 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
18685: processed = 2048 elements in 0 mSec

18688 I2C(3c) writing 30 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 c c 1d 3d 7d dd bb . Done
18688 I2C(38) writing 30 bytes to 80... fe bd fb b1 e0 c1 80 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
18688 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
18688 I2C(3c) writing 12 bytes to 0... 0 0 0 0 80 80 80 80 0 0 c0 e1 . Done
18688: processed = 2048 elements in 0 mSec

18692 I2C(3c) writing 30 bytes to 40... 7 3 3 0 2 6 e 1e 1e 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
18692 I2C(38) writing 31 bytes to 80... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
18692 I2C(3c) writing 30 bytes to 40... 0 0 0 0 0 0 0 0 0 1 8b b7 ce a7 e7 cf ee 9f 59 81 3 1 6 0 0 0 0 0 0 0 . Done
18692 I2C(3c) writing 13 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
18692: processed = 2048 elements in 0 mSec
Speed wise, I did a quick and dirty and did a digitalWriteFast(5, HIGH) at start of ISR and LOW and end. The last line in Logic analyzer trace shows this line.
Top two lines shows LA analyzer of I2C communications. and other lines were existing debug IO pin outputs 2-4

screenshot.jpg
 
Using the port read in p#12 with an altered test SSD1306 code not slamming the display I get:
Code:
494522 I2C(3c) Wr 5 B to 0... 22 0 ff 21 0 . Done
494522 I2C(3c) Wr 1 B to 0... 7f . Done
494522 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 c0 e0 e0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
494522 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
494522 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
494522 I2C(3c) Wr 1 B to 40... 0 . Done
494522: processed = 2048 elements in 0 mSec

494535 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 6 e 1e be ec 6c bf f3 bf 6f ef 7c 3c 38 18 18 0 0 0 0 0 0 . Done
494535 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
494535 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
494535 I2C(3c) Wr 11 B to 40... 0 0 0 0 0 0 0 0 0 0 0 . Done
494535: processed = 2048 elements in 0 mSec

494547 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 c f f 7 7 3 3 7 f 1f 1e 0 0 0 0 0 0 . Done
494547 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
494547 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
494547 I2C(3c) Wr 11 B to 40... 0 0 0 0 0 0 0 0 0 0 0 . Done

Using KurtE updated p#13 code I get a cleaner looking:
Code:
4350 I2C(3c) Wr 5 B to 0... 22 0 ff 21 0 . Done
4350 I2C(3c) Wr 1 B to 0... 7f . Done
4350 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 c0 e0 e0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
4350 I2C(3c) Wr 27 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
4350: processed = 2048 elements in 0 mSec

4363 I2C(3c) Wr 31 B to 40... 0 0 0 0 0 0 0 0 0 6 e 1e be ec 6c bf f3 bf 6f ef 7c 3c 38 18 18 0 0 0 0 0 0 . Done
4363 I2C(3c) Wr 16 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
4363 I2C(3c) Wr 3 B to 40... 0 0 0 . Done
4363 I2C(3c) Wr 11 B to 40... 0 0 0 0 0 0 0 0 0 0 0 . Done
4363: processed = 2048 elements in 0 mSec

4375 I2C(3c) Wr 28 B to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 c f f 7 7 3 3 7 f 1f 1e 0 0 0 . Done
4375 I2C(3c) Wr 7 B to 40... 0 0 0 0 0 0 0 . Done
4375 I2C(3c) Wr 11 B to 40... 0 0 0 0 0 0 0 0 0 0 0 . Done

The PORT way will be faster and more Atomic on the bits - the DATA looks to be sniffed the same - not sure why it triggers the SPARE ZERO chars in the output?

You can see I truncated the "I2C(3c) Wr 11 B to 40" so it better fits the screen.

I put waits in the SSD1306 test code as well to still the screen - so I added to the 'waiting for data delay' with : const int WAITING_PRINT_INTERVAL_MSEC = 1000;
 
For the fun of it I hooked it up a T4 running my FRAM test sketch and got the following which shows reading and writing to the FRAM. This is after the change to the Wire buffer lenght.

Code:
1801: Waiting for Data...
2001: Waiting for Data...
2201: Waiting for Data...
2236 I2C(7c) reading 3 bytes from a0... 0 a5 10 . Done
2236 I2C(50) writing 53 bytes to 0... 25 1 0 0 0 8f c2 ad 3f 2f cb 4 0 8e 0 0 0 50 54 68 65 20 51 75 69 63 6b 20 42 72 6f 77 6e 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
2236 I2C(50) writing 1 bytes to 0... 25 . Done
2236 I2C(50) reading 47 bytes from 0... 1 0 0 0 8f c2 ad 3f 2f cb 4 0 8e 0 0 0 50 54 68 65 20 51 75 69 63 6b 20 42 72 6f 77 6e 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 0 . Done
2236: processed = 2048 elements in 1 mSec
 
For the fun of it I hooked it up a T4 running my FRAM test sketch and got the following which shows reading and writing to the FRAM. This is after the change to the Wire buffer lenght.

Code:
1801: Waiting for Data...
2001: Waiting for Data...
2201: Waiting for Data...
2236 I2C(7c) reading 3 bytes from a0... 0 a5 10 . Done
2236 I2C(50) writing 53 bytes to 0... 25 1 0 0 0 8f c2 ad 3f 2f cb 4 0 8e 0 0 0 50 54 68 65 20 51 75 69 63 6b 20 42 72 6f 77 6e 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
2236 I2C(50) writing 1 bytes to 0... 25 . Done
2236 I2C(50) reading 47 bytes from 0... 1 0 0 0 8f c2 ad 3f 2f cb 4 0 8e 0 0 0 50 54 68 65 20 51 75 69 63 6b 20 42 72 6f 77 6e 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 0 . Done
2236: processed = 2048 elements in 1 mSec

Mike - that is running sniffer on T4? If so using which version of the SCL/SDA read p#12 or p#13?

For 'text' transfer devices it might be handy to have the typical HEX/ASCII table dump for printable chars: 65 65 65 13 09 66 66 66 ... :: A A A . . B B B …
 
Tim-that's actually using @KurtEs original form for current_portB. If I update the sketch to post #13:
Code:
3401: Waiting for Data...
3601: Waiting for Data...
3801: Waiting for Data...
3868 I2C(7c) reading 3 bytes from a0... 0 a5 10 . Done
3868 I2C(50) writing 53 bytes to 0... 25 1 0 0 0 8f c2 ad 3f 2f cb 4 0 8e 0 0 0 50 54 68 65 20 51 75 69 63 6b 20 42 72 6f 77 6e 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
3868 I2C(50) writing 1 bytes to 0... 25 . Done
3868 I2C(50) reading 47 bytes from 0... 1 0 0 0 8f c2 ad 3f 2f cb 4 0 8e 0 0 0 50 54 68 65 20 51 75 69 63 6b 20 42 72 6f 77 6e 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 0 . Done
3868: processed = 2048 elements in 0 mSec
 
Good Morning all,

Note: I pushed up my change to explicitly set the actual bits instead of assume 0, 1 and shifting... current_portb = (digitalReadFast(SCL_PIN)? 4 : 0) | (digitalReadFast(SDA_PIN)? 8 : 0);

I have not done it yet, as I already have some good I2C sniffers (Logic Analyzers...) ;)

But wonder if 1MHZ is the best speed for detecting I2C signals? Especially if you are going at lets say 400Khz? Sometimes nice if your sampling speed is close to even multiple of data speed.
So what happens if you try sampling at 2mhz?

That is what happens if you change the line:
Code:
Timer1.initialize(1); // run every mico second
to:
Code:
  Timer1.initialize(0.5); // run every half micro second

I believe IntervalTimer, at least for T4 supports a floating point number passed in to compute clocks... Have not looked at T3.x...

Note: the current stuff may be good enough for the purpose it was written!

But if one wants to be real hard core here, they could explore a few other options as well, like:

a) can the I2C hardware be somehow configured as slave spy? That is does the hardware already have some form of SPI Slave sniffer built in? I have not looked in detail on this.
b) DMA: Does it make sense to startup a continuous DMA read of the IO data register associated SCL/SDA at some real high speed, Maybe into two linked buffers, which interrupt when each is filled, and have the processing of looking at the data done at that point. Note: I would probably just have the ISRs set a flag that the main loop looks for....

c) T4 specific - Is there a FlexIO setup that makes sense to read in these two pins and package them up? Not sure we would gain much with this...

But again If it were me, I would simply try speeding up the sampling and see if it helps.

EDIT: So far not having luck setting interval timer to a faster speed! As we are not using IntervalTimer, but instead Timer1...
And besides I don't think IntervalTimer will allow a 1us interval!
 
Last edited:
As I noticed in the Edit of the above post, neither Interval TImer nor TImer1 will currently allow faster interrupt speed...

So I hacked it up slightly for T4 to use .5us interrupts...
In particular I copied the code for Timer1::Initialize (actually the set clock code). And put it in the sketch, note: Only for T4... And then I made it take a floating point value instead of int...

I then confirmed with LA that I was getting the .5us cycles...
screenshot.jpg

Also may need to see if there are counters computing time that would need to be updated....

But does show up data like:
Code:
erial available after 500 mSec
1862 I2C(3c) writing 5 bytes to 0... 22 0 ff 21 0 . Done
1862 I2C(3c) writing 1 bytes to 0... 7f . Done
1862 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
1862 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
1862 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 c f f 7 7 3 3 7 f 1f 1e 0 0 0 0 0 0 0 0 . Done
1862 I2C(3c) writing 1 bytes to 40... 0 . Done
1862: processed = 2048 elements in 0 mSec

1866 I2C(3c) writing 31 bytes to 40... e0 c0 c0 f0 78 70 30 30 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
1866 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 80 c0 c0 80 80 80 80 0 0 c0 f0 f8 f8 c0 0 0 0 0 0 0 0 0 . Done
1866 I2C(3c) writing 31 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
1866 I2C(3c) writing 11 bytes to 40... 0 0 0 0 0 0 0 0 0 0 0 . Done
1866: processed = 2048 elements in 0 mSec


Code:
/*
    Name:       Teensy_I2C_Sniffer_V11.ino
    Created:	1/18/2020 10:55:55 AM
    Author:     FRANKNEWXPS15\Frank
*/
/* 'Notes:

    A typical I2C sentence when communicating with a MPU6050 IMU module goes something like:
        "I2C(68) wrote 1 byte to 75 - C0 Done."
        "I2C(68) wrote 3 bytes to 72 - C0 0C 10 Done."
        "I2C(68) read 5 bytes from 6A - C0 0C 10 14 03 Done."

    To form a sentence, we need:
        Device addr: 68 in the above examples
        Read/Write direction
        To/From register address:  75, 72 and 6A in the above examples
        Data:  C0, C0 0C 10, and C0 0C 10 14 03 in the above examples
        number of bytes written/read:  1,3 & 5 in the above examples

     Each I2C communication proceeds as follows (assuming a START from an IDLE condition):
         A START or RESTART condition, denoted by SDA & SCL HIGH, followed by SDA LOW, SCL HIGH
         A 7-bit device address, MSB first (0x8/0xC = 1, 0x0/0x4 = 0)
         A R/W bit (0x8/0xC = read, 0x0/0x4 = write)
         An ACK bit (0x8/0xC = NAK, 0x0/0x4 = ACK)
         If the bus direction is WRITE, then
             A register address for read/write
             zero or more additional data bytes
         Else (the bus direction is READ)
            One or more additional data bytes
         Endif

    This version uses a fixed-size (2048 bytes) array instead of tonton81's circular buffer library.

    To generalize for any I2C slave device rather than just the MPU6050 IMU, comment out the
    "#define MPU6050_SPECIFIC line below. This will remove all MPU6050 specific code
*/


//#define MPU6050_SPECIFIC

#include <TimerOne.h> //needed for ISR
#ifdef MPU6050_SPECIFIC
#include "helper_3dmath.h" //Arduino\Libraries\i2cdevlib\Arduino\MPU6050\ needed to compute yaw from MPU6050 DMP packet
#endif


//#define PARSE_LOOP_DEBUG

const uint16_t CAPTURE_ARRAY_SIZE = 2048;
const uint16_t VALID_DATA_ARRAY_SIZE = 2048;
const int WAITING_PRINT_INTERVAL_MSEC = 200;//interval timer for 'Waiting for data...' printout

#define MONITOR_OUT1 2 //so can monitor ISR activity with O'scope
#define MONITOR_OUT2 3 //so can monitor ISR activity with O'scope
#define MONITOR_OUT3 4 //so can monitor ISR activity with O'scope
#define SDA_PIN 18
#define SCL_PIN 19

#pragma region PROCESSING_VARIABLES
uint8_t devAddr;
uint8_t regAddr;
uint8_t databytes[2048]; //holds multiple databytes for later output sentence construction
uint16_t numbytes = 0; //number of data bytes extracted from data stream
int ACKNAKFlag; //can be negative
uint16_t databyte_idx = 0; //index into databyte_array
uint8_t killbuff[2]; //used to consume start/stop bytes
elapsedMillis mSecSinceLastWaitingPrint;
uint8_t valid_data[2048];
uint16_t numvalidbytes = 0; //number of valid bytes in this burst
uint16_t read_idx = 0; //pointer to next byte pair to be processed

//added for bus direction labels
enum BUSDIR
{
  WRITE,
  READ,
  UNKNOWN = -1
} RWDir;
BUSDIR BusDir = BUSDIR::UNKNOWN;
#pragma endregion ProcVars


#pragma region ISR_SUPPORT
uint8_t raw_data[CAPTURE_ARRAY_SIZE]; //holds data captured from I2C bus
volatile uint16_t  write_idx = 0;
volatile uint8_t   current_portb = 0xFF;
volatile uint8_t   last_portb = 0xFF;
volatile uint16_t mult0xCCount = 0;
const uint16_t MAX_IDLE_COUNT = 2500;

volatile bool bDone = false;
volatile bool bWaitingForStart = true;
volatile bool bIsData = true;
volatile bool bIsStart = false;
volatile bool bIsStop = false;
volatile uint8_t last_current;
#pragma endregion ISR Support

#ifdef MPU6050_SPECIFIC
//01/21/20 these forward declarations are required to force the preprocessor
//to handle #ifdef MPU6050_SPECIFIC properly
uint8_t dmpGetQuaternion(int16_t* data, const uint8_t* packet);
uint8_t dmpGetQuaternion(Quaternion* q, const uint8_t* packet);
uint8_t dmpGetYawPitchRoll(float* data, Quaternion* q, VectorFloat* gravity);
uint8_t dmpGetGravity(VectorFloat* v, Quaternion* q);

Quaternion q;           // [w, x, y, z]         quaternion container
VectorInt16 aa;         // [x, y, z]            accel sensor measurements
VectorInt16 aaReal;     // [x, y, z]            gravity-free accel sensor measurements
VectorInt16 aaWorld;    // [x, y, z]            world-frame accel sensor measurements
VectorFloat gravity;    // [x, y, z]            gravity vector
float ypr[3];           // [yaw, pitch, roll]   yaw/pitch/roll container and gravity vector
float global_yawval = 0; //updated by GetIMUHeadingDeg()
#endif // MPU6050_SPECIFIC

void setup()
{
  unsigned long now = millis();
  pinMode(5, OUTPUT);
  Serial.begin(1); //rate value ignored
  int idx = 0;
  while (!Serial && (millis() - now) < 3000)
  {
    delay(500);
    idx++;
  }
  Serial.printf("Serial available after %lu mSec\n", millis() - now);

  pinMode(MONITOR_OUT1, OUTPUT);
  digitalWrite(MONITOR_OUT1, LOW);
  pinMode(MONITOR_OUT2, OUTPUT);
  digitalWrite(MONITOR_OUT2, LOW);
  pinMode(MONITOR_OUT3, OUTPUT);
  digitalWrite(MONITOR_OUT3, LOW);

  pinMode(SCL_PIN, INPUT);
  pinMode(SDA_PIN, INPUT);

  //reset port byte vars & start timer
  last_portb = current_portb = 0;
  write_idx = 0;
  memset(raw_data, 255, CAPTURE_ARRAY_SIZE);
  //PrintNextArrayBytes(raw_data, 255, 20);
#if 1
  Timer1_initialize(0.5); // run every half micro second
#else
  Timer1.initialize(1); // run every half micro second
#endif
  Timer1.attachInterrupt(capture_data);
  delay(1000);

  mSecSinceLastWaitingPrint = 0;
}
#if 1
void Timer1_initialize(float float_ms) {
  uint32_t period = (float)F_BUS_ACTUAL * float_ms * 0.0000005f;
  uint32_t prescale = 0;
  while (period > 32767) {
    period = period >> 1;
    if (++prescale > 7) {
      prescale = 7; // when F_BUS is 150 MHz, longest
      period = 32767; // period is 55922 us (~17.9 Hz)
      break;
    }
  }
  //Serial.printf("setPeriod, period=%u, prescale=%u\n", period, prescale);
  FLEXPWM1_FCTRL0 |= FLEXPWM_FCTRL0_FLVL(8); // logic high = fault
  FLEXPWM1_FSTS0 = 0x0008; // clear fault status
  FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(8);
  FLEXPWM1_SM3CTRL2 = FLEXPWM_SMCTRL2_INDEP;
  FLEXPWM1_SM3CTRL = FLEXPWM_SMCTRL_HALF | FLEXPWM_SMCTRL_PRSC(prescale);
  FLEXPWM1_SM3INIT = -period;
  FLEXPWM1_SM3VAL0 = 0;
  FLEXPWM1_SM3VAL1 = period;
  FLEXPWM1_SM3VAL2 = 0;
  FLEXPWM1_SM3VAL3 = 0;
  FLEXPWM1_SM3VAL4 = 0;
  FLEXPWM1_SM3VAL5 = 0;
  FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_LDOK(8) | FLEXPWM_MCTRL_RUN(8);
 // pwmPeriod = period;
}

#endif


//-------------------------------------------------------------------------------
//--------------------------------    ISR    ------------------------------------
//-------------------------------------------------------------------------------
FASTRUN void capture_data()
//void capture_data()
{
  digitalWriteFast(5, HIGH);
  last_portb = current_portb;
#if defined(__IMXRT1062__)
  current_portb = (digitalReadFast(SCL_PIN) << 2) | (digitalReadFast(SDA_PIN) << 3);
  current_portb = (digitalReadFast(SCL_PIN) ? 4 : 0) | (digitalReadFast(SDA_PIN) ? 8 : 0);
#else
  current_portb = GPIOB_PDIR & 12; //reads state of SDA (18) & SCL (19) at same time
#endif
  if (!bDone && last_portb != current_portb)
  {
    mult0xCCount = 0; //reset IDLE counter
    digitalWriteFast(MONITOR_OUT1, HIGH);

    //01/17/20: joepasquariello suggestion
    last_current = (last_portb << 4) | (current_portb);
    bIsStart = (last_current == 0xC4);
    bIsStop = (last_current == 0x4C);
    bIsData = (last_current == 0x04) || (last_current == 0x8C);

    if (bIsStart) //START
    {
      digitalWriteFast(MONITOR_OUT2, HIGH);
      if (bWaitingForStart)
      {
        digitalWriteFast(MONITOR_OUT3, HIGH); //start of entire capture
        bWaitingForStart = false;
      }
    }
    else if (bIsStop) //STOP
    {
      digitalWriteFast(MONITOR_OUT2, LOW);
    }

    if (!bWaitingForStart && (bIsData || bIsStart || bIsStop))
    {
      //digitalWriteFast(MONITOR_OUT3, HIGH);
      raw_data[write_idx] = last_portb;
      write_idx++;
      raw_data[write_idx] = current_portb;
      write_idx++;
      if (write_idx >= CAPTURE_ARRAY_SIZE)
      {
        bDone = true;
        digitalWriteFast(MONITOR_OUT3, LOW);
      }
    }
    digitalWriteFast(MONITOR_OUT1, LOW);
  }
  else if (!bDone && mult0xCCount < MAX_IDLE_COUNT && last_portb == 0xc && current_portb == 0xc)
  {
    mult0xCCount++;
    if (mult0xCCount >= MAX_IDLE_COUNT)
    {
      digitalWriteFast(MONITOR_OUT3, LOW);
      bDone = true;
    }
  }
  digitalWriteFast(5, LOW);
}
//-------------------------------------------------------------------------------
//-------------------------------- END ISR    ---------------------------------
//-------------------------------------------------------------------------------

void loop()
{
  if (bDone)
  {
    if (write_idx > 14)
    {
      //OK, we have some data to process. IDLE detection must have been EOM
      Timer1.stop();

      unsigned long startMsec = millis();

      //Serial.printf("%lu\t %d\t", millis(), write_idx);
      //PrintNextArrayBytes(raw_data, 0, 50);
      //Serial.printf(" - %lu\n", millis());
      uint16_t numprocessed = DecodeAndPrintValidData(raw_data); //decode and print everything captured so far
      unsigned long endMsec = millis();
      Serial.printf("%lu: processed = %d elements in %lu mSec\n\n", startMsec, numprocessed, endMsec - startMsec);

      Timer1.start();
    }

    read_idx = 0;
    bDone = false;
    mult0xCCount = 0;
    write_idx = 0;
    bWaitingForStart = true;
  }
  else
  {
    //no data to process, but don't blow prints out every mSec...
    if (mSecSinceLastWaitingPrint > WAITING_PRINT_INTERVAL_MSEC)
    {
      mSecSinceLastWaitingPrint -= WAITING_PRINT_INTERVAL_MSEC;
      Serial.printf("%lu: Waiting for Data...\n", millis());
    }
  }
}

void PrintNextArrayBytes(uint8_t* data, uint16_t startidx, uint16_t numbytes)
{
  Serial.printf("%d bytes starting at %d: ", numbytes, startidx);
  for (uint16_t i = 0; i < numbytes; i++)
  {
    Serial.printf("%x ", data[i + startidx]);
  }
}


uint16_t DecodeAndPrintValidData(byte* data)
{
  //Purpose:  decode and print I2C conversation held in raw_data array
  //Inputs:
  //  cb = 2048 element FIFO
  //Outputs:
  //  returns number of bytes processed, or -1 for failure
  //  outputs structured I2C sentence to serial monitor
  //Plan:
  //  Step1: Cull out invalid bytes
  //  Step2: Determine if there is anything to do (have to have more than one transition in FIFO)
  //  Step3: Parse transitions into I2C sentence structure
  //  Step4: Output sentence to serial monitor

  memset(valid_data, 0, VALID_DATA_ARRAY_SIZE);
#ifdef PARSE_LOOP_DEBUG
  PrintNextArrayBytes(valid_data, 0, 20); //print out first 20 bytes for verification
#endif
  numvalidbytes = RemoveInvalidBytes(raw_data, valid_data);
#ifdef PARSE_LOOP_DEBUG
  Serial.printf("Removed %d invalid bytes, leaving %d remaining\n", write_idx + 1 - numvalidbytes, numvalidbytes);
  PrintNextArrayBytes(valid_data, 0, numvalidbytes); //print out first 20 bytes of valid_data array
#endif


  if (numvalidbytes < 2)
  {
    return 0;
  }

  while (read_idx < numvalidbytes)
  {
#ifdef PARSE_LOOP_DEBUG
    Serial.printf("At top of while (read_idx < numvalidbytes): read_idx = %d\n", read_idx);
    Serial.printf("Next two bytes in valid_data are %x, %x\n", valid_data[read_idx], valid_data[read_idx + 1]);
#endif
    //Find a START sequence (0xC followed by 0x4)
    while (!IsStart(valid_data, read_idx) && read_idx < numvalidbytes)
    {
      //Serial.printf("looking for start...\n");
      read_idx++;
    }
    //at this point, read_idx should point to next valid byte pair

#ifdef PARSE_LOOP_DEBUG
    Serial.printf("Start sequence found at %d\n", read_idx - 2);
    //PrintNextFIFOBytes(valid_data, 20);
#endif

    if (numvalidbytes - read_idx > 14)//14 entries required for 7-bit address
    {
      //Get 7-bit device address
      devAddr = Get7BitDeviceAddr(valid_data, read_idx);
#ifdef PARSE_LOOP_DEBUG
      Serial.printf("devAddr = %x\n", devAddr);
#endif
    }
    else
    {

#ifdef PARSE_LOOP_DEBUG
      Serial.printf("ran out of data at readidx = %d - exiting!\n", read_idx);
#endif
      break;
    }

    //get read/write flag  1 = Read, 0 = Write, -1 = error
    BusDir = (BUSDIR)GetReadWriteFlag(valid_data, read_idx);

#ifdef PARSE_LOOP_DEBUG
    Serial.printf("BusDir = %s\n", ((BusDir == BUSDIR::WRITE) ? "WRITE" : "READ"));
    //PrintNextFIFOBytes(valid_data, 20);
#endif

    //get ACK/NAK flag
    ACKNAKFlag = GetACKNAKFlag(valid_data, read_idx);
    numbytes = GetDataBytes(valid_data, read_idx, databytes); //terminates on a START, but the start bytes are not consumed
#ifdef PARSE_LOOP_DEBUG
    Serial.printf("Got %d bytes from GetDataBytes() --> ", numbytes);
    for (size_t i = 0; i < numbytes; i++)
    {
      Serial.printf(" %x ", databytes[i]);
    }
    Serial.printf("\n");

    //PrintNextFIFOBytes(cb_trans, 20);
#endif
    //If the bus direction is WRITE, then extract
    //    A register address for read / write
    //    zero or more additional data bytes
    if (BusDir == BUSDIR::WRITE)
    {
      regAddr = databytes[0];
#ifdef PARSE_LOOP_DEBUG
      Serial.printf("regAddr = %x, read_idx = %d\n", regAddr, read_idx);
#endif

      //check for additional data
      if (numbytes > 1)
      {
#ifdef PARSE_LOOP_DEBUG
        Serial.printf("Additional data found!\n");
        for (size_t i = 0; i < numbytes; i++)
        {
          Serial.printf("data[%d] = %x\n", i, databytes[i]);
        }
#endif
        //1st byte is register addr, subsequent bytes are data
        OutputFormattedSentence(BusDir, devAddr, regAddr, numbytes, databytes, 1);
      }
    }
    else  //all bytes are data
    {
#ifdef PARSE_LOOP_DEBUG
      Serial.printf("In data block:  got %d bytes of data\n", numbytes);
      for (size_t i = 0; i < numbytes; i++)
      {
        Serial.printf("data[%d] = %x\n", i, databytes[i]);
      }
#endif
      OutputFormattedSentence(BusDir, devAddr, regAddr, numbytes, databytes, 0);
    }
#ifdef PARSE_LOOP_DEBUG
    Serial.printf("At end of while (read_idx < numvalidbytes): read_idx = %d\n", read_idx);
#endif

  }//while (read_idx < numvalidbytes)
  return numvalidbytes;
}


#pragma region Support Functions
bool IsStart(byte* data, uint16_t& readidx)
{
  bool result = false;

  //Serial.printf("IsStart[%d] = %x, IsStart[%d] = %x\n",
  //    readidx, data[readidx], readidx + 1, data[readidx + 1]);

  if (data[readidx] == 0xC && data[readidx + 1] == 0x4)
  {
    result = true;
    readidx += 2; //bump to next byte pair
  }
  return result;
}

bool IsStop(byte* data, uint16_t& readidx)
{
  bool result = false;

  //Serial.printf("IsStop[%d] = %x, IsStop[%d] = %x\n",
  //readidx, data[readidx], readidx + 1, data[readidx + 1]);

  if (data[readidx] == 0x4 && data[readidx + 1] == 0xC)
  {
    result = true;
    readidx += 2; //bump to next byte pair
  }
  return result;
}

uint8_t Get7BitDeviceAddr(byte* data, uint16_t& readidx)
{
  //Purpose: Construct a 7-bit address starting from dataidx
  //Inputs:
  //  data = pointer to valid data array
  //  readidx = starting index of 7-bit address sequence (MSB first)
  //Outputs:
  //  returns the address as an 8-bit value with the MSB = 0, or 0x0 if unsuccessful
  //  dataidx = pointer to next data entry
  //Plan:
  //  Step1: Convert a pair of data entries into a 0 or 1
  //  Step2: Add the appropriate value to an ongoing sum
  //  Step3: return the total.
  //Notes:
  //  A '0' is coded as a 0x0 followed by a 0x4
  //  A '1' is coded as a 0x8 followed by a 0xC

  uint8_t devAddr = 0x0; //failure return value

  //Serial.printf("Get7BitDeviceAddr: readidx = %d\n",readidx);

  //devAddr is exactly 7 bits long, so 8 bits with MSB = 0
  for (size_t i = 0; i < 7; i++)
  {
    if (data[readidx] == 0x0 && data[readidx + 1] == 0x4)
    {
      readidx += 2; //advance the pointer, but don't add to sum
    }

    else if (data[readidx] == 0x8 && data[readidx + 1] == 0xC)
    {
      //Serial.printf("Get7BitDeviceAddr: '1' found at i = %d, adding %x to devAddr to get %x\n",
      //    i, 1 << (7 - i), devAddr + (1 << (7-i)));

      readidx += 2; //advance the pointer
      devAddr += (1 << (7 - i)); //add 2^(7-i) to sum
    }
  }

  devAddr = devAddr >> 1; //divide result by 2 to get 7-bit addr from 8 bits
  return devAddr;
}

int Get8BitDataByte(byte* data, uint16_t& readidx)
{
  //Purpose: Construct a 8-bit data byte starting from dataidx
  //Inputs:
  //  data = pointer to valid data array
  //  readidx = starting index of 8-bit data byte (MSB first)
  //Outputs:
  //  returns the address as an 8-bit value, or 0x0 if unsuccessful
  //  dataidx = pointer to next data entry
  //Plan:
  //  Step1: Convert a pair of data entries into a 0 or 1
  //  Step2: Add the appropriate value to an ongoing sum
  //  Step3: return the total.
  //Notes:
  //  A '0' is coded as a 0x0 followed by a 0x4
  //  A '1' is coded as a 0x8 followed by a 0xC
  //  12/29/19 - changed return val to int, so can return -1 when a 'short byte' is detected

  int dataval = 0x0; //failure return value

#ifdef GET_8BIT_DATABYTE_DEBUG
  Serial.printf("Get8BitDataByte: data[%d] = %x, data[%d] = %x\n",
                readidx, data[readidx], readidx + 1, data[readidx + 1]);
#endif

  //8 bits with MSB = 0
  int numbytes = 0;
  for (size_t i = 0; i < 8; i++)
  {
    if (data[readidx] == 0x0 && data[readidx + 1] == 0x4)
    {
      readidx += 2; //advance the pointer, but don't add to sum
      numbytes++;
    }

    else if (data[readidx] == 0x8 && data[readidx + 1] == 0xC)
    {
#ifdef GET_8BIT_DATABYTE_DEBUG
      Serial.printf("Get8BitDataByte: '1' found at i = %d, adding %x to devAddr to get %x\n",
                    i, 1 << (7 - i), dataval + (1 << (7 - i)));
#endif
      readidx += 2; //advance the pointer
      dataval += (1 << (7 - i)); //add 2^(8-i) to sum
      numbytes++;
    }
  }

#ifdef GET_8BIT_DATABYTE_DEBUG
  Serial.printf("Get8BitDataByte: numbytes = %d\n", numbytes);
#endif
  if (numbytes != 8)
  {
    dataval = -1; //error return value
  }

  return dataval;
}

int GetReadWriteFlag(byte* data, uint16_t& readidx)
{
  //Purpose: decode R/W byte pair
  //Inputs:
  //  data = pointer to valid data array
  //  readidx = index into data to start of R/W byte pair
  //Outputs:
  //  readidx = if successful, points to next byte pair in data
  //  returns 1 for Read (0x8/0xC), 0 for Write (0x0/0x4), -1 for failure
  //Notes:
  //

  //Serial.printf("GetReadWriteFlag: readidx = %d, data[readidx] = %x, data[readidx+1]= %x\n",
  //    readidx, data[readidx], data[readidx + 1]);
  int result = 0;
  if (data[readidx] == 0x8 && data[readidx + 1] == 0xC)
  {
    result = 1; //read detected
    readidx += 2; //point to next byte pair
  }

  else if (data[readidx] == 0x0 && data[readidx + 1] == 0x4)
  {
    result = 0; //write detected
    readidx += 2; //point to next byte pair
  }
  else
  {
    result = -1; //failed to detect read or write
  }

  return result;
}

int GetACKNAKFlag(byte* data, uint16_t& readidx)
{
  //Purpose: decode ACK/NAK byte pair
  //Inputs:
  //  data = pointer to valid data array
  //  readidx = index into data to start of ACK/NAK byte pair
  //Outputs:
  //  readidx = if successful, points to next byte pair in data
  //  returns 1 for NAK (0x8/0xC), 0 for ACK (0x0/0x4), -1 for failure
  //Notes:
  //

  //Serial.printf("GetACKNAKFlag: readidx = %d, data[readidx] = %x, data[readidx+1]= %x\n",
  //    readidx, data[readidx], data[readidx + 1]);
  int result = 0;
  if (data[readidx] == 0x8 && data[readidx + 1] == 0xC)
  {
    result = 1; //NAK detected
    readidx += 2; //point to next byte pair
  }

  else if (data[readidx] == 0x0 && data[readidx + 1] == 0x4)
  {
    result = 0; //ACK detected
    readidx += 2; //point to next byte pair
  }
  else
  {
    result = -1; //failed to detect ACK or NAK
  }

  return result;
}

int GetDataBytes(uint8_t* data, uint16_t& readidx, uint8_t* databytes)
{
  //Notes:
  //  01/01/2020: removed databyteidx from sig - always starts at zero

  uint16_t numbytes = 0;
  uint16_t databyte_idx = 0;

  bool StartFlag = false;
  bool StopFlag = false;

  do
  {
    int dataval = Get8BitDataByte(data, readidx);

    //watch out for 'short byte' reads
    if (dataval >= 0)
    {
      uint8_t databyte = (uint8_t)dataval;
      databytes[databyte_idx] = databyte;
      databyte_idx++;
      numbytes++;
    }

    ACKNAKFlag = GetACKNAKFlag(data, readidx);
    StartFlag = IsStart(data, readidx);
    StopFlag = IsStop(data, readidx);

#ifdef PARSE_LOOP_DEBUG
    Serial.printf("IsStart returned %d, IsStop returned %d, dataidx = %d\n",
                  StartFlag, StopFlag, readidx);
#endif

  } while (!StartFlag && !StopFlag && readidx < numvalidbytes);


  readidx -= 2;//back readidx up so loop top is positioned correctly.

  return numbytes;
}

void OutputFormattedSentence(int RW, uint8_t dev, uint8_t reg, uint8_t numbytes, uint8_t* bytearray, uint16_t startidx)
{
  Serial.printf("%lu I2C(%x) %s %d bytes %s %x... ",
                millis(), dev, (RW == 0 ? "writing" : "reading"),  numbytes - startidx, (RW == 0 ? "to" : "from"), reg);
  for (size_t i = startidx; i < numbytes; i++)
  {
    Serial.printf("%x ", bytearray[i]);
  }
  Serial.printf(". Done\n");

  //#ifdef MPU6050_SPECIFIC
  //
  //
  //    //01/18/20 experiment to decode 28-byte packet into yaw value
  //    if (numbytes == 28)
  //    {
  //        dmpGetQuaternion(&q, bytearray);
  //        dmpGetGravity(&gravity, &q);
  //        dmpGetYawPitchRoll(ypr, &q, &gravity);
  //
  //        //compute the yaw value
  //        global_yawval = ypr[0] * 180 / M_PI;
  //        Serial.printf("yawval = %3.2f\n", global_yawval);
  //    }
  //    else
  //    {
  //        Serial.printf(". Done\n");
  //    }
  //#endif // MPU6050_SPECIFIC



}

uint16_t RemoveInvalidBytes(uint8_t* rawdata, uint8_t* validdata)
{
  uint16_t numvalid = 0;
  uint16_t valididx = 0;

  //Serial.printf("raw data array contains %d bytes\n", write_idx + 1);
  //PrintNextArrayBytes(raw_data, 0, 20);

  //OK, now go back through the array, excising invalid sequences
  for (uint16_t rawidx = 0; rawidx < write_idx;/*rawidx incremented internally*/)
  {
    uint8_t firstByte = raw_data[rawidx]; //get the first byte
    uint8_t secondByte = raw_data[rawidx + 1]; //get the next byte
    bool validpair =
      (
        (firstByte == 0xC && secondByte == 0x4) //START or RESTART
        || (firstByte == 0x4 && secondByte == 0xC) //STOP
        || (firstByte == 0x0 && secondByte == 0x4) //0 OR ACK
        || (firstByte == 0x8 && secondByte == 0xC) //1 or NAK
      );

    //Serial.printf("rawidx %d: Considering %x and %x: validity = %d\n",
    //rawidx, firstByte, secondByte, validpair);
    if (validpair)
    {
      //save valid bytes to valid_bytes array
      validdata[valididx] = firstByte;
      validdata[valididx + 1] = secondByte;
      numvalid += 2;
      //Serial.printf("Added %x & %x at idx = %d & %d\n", firstByte, secondByte, valididx, valididx + 1);
      //PrintNextArrayBytes(validdata,0,numvalid);
      rawidx += 2;
      valididx += 2;
    }
    else
    {
      rawidx++; //on invalid, just go to next byte
    }
  }

  return numvalid;
}
#pragma endregion Support Functions

#ifdef MPU6050_SPECIFIC

//#pragma region YAW_COMPUTATIONS
////01/18/2020: I copied these functions from MPU6050_6Axis_MotionApps_V6_12.h and
////  modified them to be called directly instead of from an 'mpu' object
//
uint8_t dmpGetQuaternion(int16_t* data, const uint8_t* packet)
{
  // TODO: accommodate different arrangements of sent data (ONLY default supported now)
  if (packet != 0)
  {
    data[0] = ((packet[0] << 8) | packet[1]);
    data[1] = ((packet[4] << 8) | packet[5]);
    data[2] = ((packet[8] << 8) | packet[9]);
    data[3] = ((packet[12] << 8) | packet[13]);
  }
  return 0;
}

uint8_t dmpGetQuaternion(Quaternion* q, const uint8_t* packet)
{
  // TODO: accommodate different arrangements of sent data (ONLY default supported now)
  int16_t qI[4] { 0, 0, 0, 0 };
  uint8_t status = dmpGetQuaternion(qI, packet);
  if (status == 0) {
    q->w = (float)qI[0] / 16384.0f;
    q->x = (float)qI[1] / 16384.0f;
    q->y = (float)qI[2] / 16384.0f;
    q->z = (float)qI[3] / 16384.0f;
    return 0;
  }
  return status; // int16 return value, indicates error if this line is reached
}

uint8_t dmpGetYawPitchRoll(float* data, Quaternion* q, VectorFloat* gravity)
{
  // yaw: (about Z axis)
  data[0] = atan2(2 * q->x * q->y - 2 * q->w * q->z, 2 * q->w * q->w + 2 * q->x * q->x - 1);
  // pitch: (nose up/down, about Y axis)
  data[1] = atan2(gravity->x, sqrt(gravity->y * gravity->y + gravity->z * gravity->z));
  // roll: (tilt left/right, about X axis)
  data[2] = atan2(gravity->y, gravity->z);
  if (gravity->z < 0) {
    if (data[1] > 0) {
      data[1] = PI - data[1];
    }
    else {
      data[1] = -PI - data[1];
    }
  }
  return 0;
}


uint8_t dmpGetGravity(VectorFloat* v, Quaternion* q)
{
  v->x = 2 * (q->x * q->z - q->w * q->y);
  v->y = 2 * (q->w * q->x + q->y * q->z);
  v->z = q->w * q->w - q->x * q->x - q->y * q->y + q->z * q->z;
  return 0;
}
//#pragma endregion YAW_COMPUTATIONS
#endif //MPU6050_SPECIFIC
 
@KurtE
Nice work on the timer. :)

I did a modification to the FRAM lib to write any number of bytes but its not working, can confirm this without the i2csniffer but I hooked it up any way with the following results:
Code:
14292 I2C(7c) reading 3 bytes from a0... 0 a5 10 . Done
14292 I2C(50) writing 5 bytes to 0... 25 1 0 0 0 . Done
14292 I2C(1e) reading 25 bytes from 0... 9 b4 fc bd 2c 10 1 38 0 0 0 40 50 a0 94 80 44 d4 a4 8c ac 80 8 c8 bc . Done
14292 I2C(50) writing 21 bytes to 0... 25 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 . Done
14292 I2C(50) writing 1 bytes to 0... 25 . Done
14292 I2C(50) reading 46 bytes from 0... 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 20 51 75 69 63 6b 20 42 72 6f 77 6e 20 46 6f 78 0 0 0 0 0 0 0 0 0 0 . Done
14291: processed = 2048 elements in 1 mSec
 
Status
Not open for further replies.
Back
Top