Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 21 of 21

Thread: Teensy I2C Sniffer for MPU6050

  1. #1

    Teensy I2C Sniffer for MPU6050

    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

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,633
    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_V 11.ino:37:120: fatal error: helper_3dmath.h: No such file or directory
    compilation terminated.
    www gives me that from :: https://github.com/ElectronicCats/mp...elper_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_V 11.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\Mi crosoft.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\Mi crosoft.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\Mi crosoft.CppBuild.targets(382,5): error MSB4018: at System.Text.StringBuilder.AppendFormatHelper(IForm atProvider provider, String format, ParamsArray args)
    1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Mi crosoft.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\Mi crosoft.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\Mi crosoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.Shared.ResourceUtilities.FormatStr ing(String unformatted, Object[] args)
    1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Mi crosoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.Utilities.TaskLoggingHelper.Format String(String unformatted, Object[] args)
    1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Mi crosoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.Utilities.TaskLoggingHelper.Format ResourceString(String resourceName, Object[] args)
    1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Mi crosoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.Utilities.TaskLoggingHelper.LogErr orWithCodeFromResources(String messageResourceName, Object[] messageArgs)
    1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Mi crosoft.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\Mi crosoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.BackEnd.TaskExecutionHost.Microsof t.Build.BackEnd.ITaskExecutionHost.Execute()
    1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Mi crosoft.CppBuild.targets(382,5): error MSB4018: at Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstan tiatedTask>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.

  3. #3
    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

  4. #4
    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

  5. #5
    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

  6. #6
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,107
    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...

  7. #7
    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

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,633
    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.

  9. #9
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,107
    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

  10. #10
    defragster,

    Good suggestion. I edited the ReadMe to include the links

  11. #11
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,633
    Quote Originally Posted by KurtE View Post
    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.

  12. #12
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,633
    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;
        */
    
    #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;
    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 by defragster; 01-22-2020 at 08:11 PM.

  13. #13
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,107
    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

    Click image for larger version. 

Name:	screenshot.jpg 
Views:	3 
Size:	68.4 KB 
ID:	18812

  14. #14
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,633
    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;

  15. #15
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,619
    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

  16. #16
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,633
    Quote Originally Posted by mjs513 View Post
    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 …

  17. #17
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,619
    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

  18. #18
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,107
    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 by KurtE; 01-23-2020 at 03:08 PM.

  19. #19
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,107
    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...
    Click image for larger version. 

Name:	screenshot.jpg 
Views:	4 
Size:	48.0 KB 
ID:	18830

    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

  20. #20
    Senior Member+ mjs513's Avatar
    Join Date
    Jul 2014
    Location
    New York
    Posts
    4,619
    @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

  21. #21
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    6,107
    Thanks, but wondering if TeensyTimerTool might work here?

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •