Using I2C for SMBus device communications

@ScruffR @pra Thanks for hanging around and seeing this all the way through.

I understand most of what you have told me to do but still not clear on a few things.

Here is what I do understand:

  1. I added the revised readString function code to the .cpp file so it included the return len; at the end of the function.

I tried to follow your instructions as much as it can but honestly I’m having trouble comprehending what everything means and how it fits together. Usually I find a working library, look at the patterns and see how things are working and then I’m able to add, change, or delete functions to get the program to suit my needs.

It looks your your way of implementing the readString is a little different than the way the other functions are being called or used for the read word and read block functions. This is causing mass confusion in my brain :hurtrealbad:

So what I have done is create a Github page for the code were building out together here. Its all I have so far and its a work in progress. I’ve basically just been working on getting the functions right with the readString being the last function. Once I have templates of all strings then I will be able to fill in the rest of the code to read all the avaliable registers of this particular fuel gauge.

@pra Here is the SBS Datasheet for the actual fuel gauge I’m using: http://www.ti.com/lit/ug/sluuan7a/sluuan7a.pdf

Here is the link to the Github code listing: https://github.com/SolarDude/i2cHelp

If you could fix what I’m doing wrong I should be able to then look it over and understand better how this has to be structured to work properly. It drives me nuts to not fully understand what your telling me I should do but I’m also sure you didn’t learn all this in just 5 days either.

Let me know what you think guys!

:smiley:

@RWB,

Looking at your code, you need to make a couple of adjustments:

  • readString is defined in your header file as a public class method. That is per @ScruffR advise and allows the bms.readString() in BQ20Z45_ExampleCode.ino to work. However, it must be implemented as such in the cpp file.

  • Class method implementations have the class_name:: before their name. Private functions are implemented just using their name, but can only be referenced within the class. So you need to:
    . Move line 317 and replace line 87 with it. That will make readString a public method.

  • You need to sort out your includes. You definitely don’t want to include arduino.h in BQ20Z45.cpp and you definitely don’t want to include wire.h in the ino file. I don’t know for sure as I don’t use the Spark/Arduino development model or tools, but I think with a ino file application.h is inserted automatically by the preprocessor in the Web Ide. Maybe @ScruffR can clarify.

  • You should now be able to compile and run. When you are comfortable with the results I recommend you do the following to make things more consistent.

  • Make readString a private function just like the other read functions (move the readString prototype declaration in the ,h file from the public section to the private; remove BQ20Z45:: from the readString implementation in the .cpp file

  • Add a class method int GetManufacturerName(char *result) that then calls the private method as such - return(readString(BQ20Z45_ManufactureName, result); You add a new public prototype in .h and a new class method (int BQ20Z45::GetManufacturerName(char *result) to the .cpp file.

  • You can then call do bms.GetManufactuerName(strBuffer) in the .ino file to replace the bms.readString() which will no longer work.

  • Then if there are other strings you want to retrieve and display from the device, you can repeat the above process to implement each of them as class methods that use the private readString function to retrieve them,

Hope this is of help

1 Like

@pra @ScruffR

I made the changes Peter suggested in the first 2 lines and indeed it did compile. It also successfully returns the ASCII data from the register, see the serial data output below:

I can't thank you guys enough for the help with this :smile:

Your teaching a man to fish instead of just feeding me what I need.

Learning this C++ programming can sometimes have me looking and feeling like this:

Now I'm going to attempt to make the other changes that @pra suggested because its exactly what I was wanting simply because its seems to make sense to me.

I'll be right back.

2 Likes

Peter (@pra), thanks for "translating" my waffling into human language :wink:

And you are absolutely right in this!

In .ino files the preprocessor does some stuff like adding application.h, inserting function prototypes if required and some C++ stuff. But sometimes it messes things up, then you'd need to add #pragma SPARK_NO_PREPROCESSOR and do all the other things yourself (e.g. add 'application.h').
If you want to use Spark Core wiring functions in a '.h/.cpp' you'd need to add application.h manually.

@Pra @ScruffR

I was also successful in setting up the new structure for calling readString just as I has been calling the read word and read block functions. Everything makes much more sense now that I can see the pattern for calling all the above I2C read functions.

I’m so glad that is out of the way now :wink:

///////////////////////////////////////////////////////////////////////////////

Now lets look at the string data that we are pulling.

    1. Some of the String Data is in ASCII format.

Here is the serial print data that is returned to for the above registers. It’s working just fine.

-2. Some of the registers return data what looks like Hex.

Now here is what I get when I serial print these Lifetime 1 & 2 registers.

I’m using this code to serial print the lines above:

void loop()
{
 
    Serial.print("LifetimeDataBlock1: ");
    bms.GetLifetimeDataBlock1(strBuffer);
    Serial.println(strBuffer);
    
    Serial.print("LifetimeDataBlock2: ");
    bms.GetLifetimeDataBlock2(strBuffer);
    Serial.println(strBuffer);



    Serial.println();
    delay(3000); // Show new results every second.
  
}

I tried adding the ,Hex and ,Bin but didn’t have any success in getting any of the data shown properly.

I did a LA scan of the communication log when reading the LifetimeDataBlock1 register and we are getting alot of data back. See below:

Decoded Protocol Result
Setup Write to [0x16] + ACK
0x60 + ACK
Setup Read to [0x16] + ACK
0x20 + NAK
Setup Write to [0x16] + ACK
0x60 + ACK
Setup Read to [0x16] + ACK
0x20 + ACK
0x90 + ACK
0x91 + ACK
0xB4 + ACK
0x91 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x00 + ACK
0x7E + ACK
0x7E + ACK
0x75 + ACK
0x7E + ACK
0xFF + ACK
0xFF + ACK
0xFF + ACK
0xFF + ACK
0xFF + ACK
0xFF + ACK
0xFF + ACK
0xFF + ACK
0xFF + ACK
0xFF + ACK
0xFF + NAK

This is what each byte is referring to in the LifeTimeDataBlock1 return.

So how can I get this data to serial print?

I’m guessing I’m going to need to create some more code to process each byte in the these string array returns as needed. Like the first byte returned is the Maximum Voltage of battery cell #1 and the returned number between 0 and 255 needs to be *20 to get the correct battery cell voltage.

So I’m pretty sure you were trying to tell me how to focus on specific bytes in a array using the code and advice commented in below:

The advice above makes sense to me.

If I wanted to Serial Print the first returned byte from the LifeTimeDataBlock1 in a number format between 0-255 *20 to give me the voltage in mV ? Once I have this figured out I’ll be able to use that template code to print all the other data points that are returned.

Again Thanks a Million for the Help!

@RWB, even if the docs state this is a string, it actually isn’t a “propper string” as such, so you can’t just printlin() it.
This is rather a bunch of 32 single bytes, which you’d have to print out individually.

Your attempt to only add the HEX & BIN to the println() statement didn’t work since there are different overloads of this function.
Overloads are different implementations for one function name (in this case println) with different signatures (return type & parameter list).
So you have used the overload that does expect a const char* as one and only input parameter.
For the HEX/BIN format parameter to work, you’d have to call an overload that takes two parameters wher the first on can only by a number type #

    size_t print(unsigned char, int = DEC);
    size_t print(int, int = DEC);
    size_t print(unsigned int, int = DEC);
    size_t print(long, int = DEC);
    size_t print(unsigned long, int = DEC);
    size_t print(double, int = BIN);

To get a decent result from your return, you could do this

  int len = bms.GetLifetimeDataBlock1(strBuffer);
  for (int i = 0; i < len, i++)
  {
    Serial.print(i+1);       // ordinal number of reading
    Serial.print(". \t");    // add a dot and a tabulator
    Serial.print(((uint8_t)strBuffer[i]) * 0.020);
    Serial.println("V");     // Volt plus new line
  }

But it get’s more and more apparent that you could do with a very basic C/C++ tutorial before taking on an endeavour like this.
Copy/Pasting without the actual background will not work for long.

@RWB,

Ryan, there are some significant issues trying to process the Lifetime data blocks. @ScruffR is spot on telling you this is not “string” data. Also if you read the spec, this information is supposed to retrieved via the Manufacturer_Access() or Manufacturer_Block_Access() interface. These are very complicated interfaces that involve multi-byte writes followed by block reads, and frankly would require more time than I want to devote helping you with, If you look at the data returned in your LA output from your “raw” read of the lifetime data block 1, it doesn’t match the spec, so there is obviously some manipulation done in the Manufacturer_Access(0 command.

Setup Write to [0x16] + ACK
0x60 + ACK
Setup Read to [0x16] + ACK
0x20 + ACK
0x90 + ACK
0x91 + ACK
0xB4 + ACK
0x91 + ACK

So, there are 0x20 or 32 bytes of data read, actually 33 including the length byte. However, the Spark I2c implementation limits you to a maximum of 32 bytes so you can never read the 33 or more bytes, so there will be lost data.

The next values are the current voltage values for what looks like a 4S LIPO in 20mv increments so 2.88 (144 * .02), 2.90, 2.64 and 2.90 respectively.

If you look further down:

0x7E + ACK
0x7E + ACK
0x75 + ACK
0x7E + ACK

Then these are the minimum voltages for these 4 cells (2.52, 2.52, 2.34, 2.52).

I strongly recommend you stay away from the manufacturer_access stuff and limit yourself to what’s available elsewhere To process this type of information you need to change the buffer’s data type from char to unsigned byte. So:

         uint8_t         *dta;
         int             inx;
         
         bms.getSomething(address, stringBuffer);
         dta = (uint8_t *) stringBuffer;

         inx = 0;
         serial.print("cell voltages:");
         while((dta[inx] != 0) && (inx < 15)) {
                 if (inx)
                       serial.print(", ");
                 serial.print(dta[inx++] * 20);         // will be in millivolts. not volts
         }
         serial.println("");

This should be enough to give you the gist.

More complex block data may require casting to a structure rather than bytes or integer arrays. I suggest you follow @ScruffR’s recommendation that you tackle some basic C/C++ tutorials before attempting to go there.

1 Like

Hey guys @pra @ScruffR just wanted to give a quick update.

About the Strings not being proper strings. I now see how the Lifetime Data Blocks are returning 1 and 2 byte signed and unsigned data.

I can now see why simply adding HEX or BIN to the println function will not work right.

I totally agree with you on this. I'm just very limited on free time so I just try to work on one thing at a time with the help of others on forums and Youtube videos usually. This chip is way more advanced than most of the other stuff I have worked with up till now but all this trouble shooting has really taught me a lot about how I2C works and that is very useful.

I have been looking over this for the last 24 hours and I'm happy to say that we can access the LifeTime Data blocks without needing to create new code to access the Manufacturer_Access() or Manufacturer_Block_Access() because its made available via 2 different methods.

The Manufacturer_Access and Block Access provide 3 Registors that return more than 32 bytes of data which as you say causes issues with the Spark Core and Arduino's which are limited to 32 byte data types. Below are the LifetimeData Registers that can be access via Manufacturer_Access and Block Access Commands. Note there are only 3 Registers.

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

The good news is that the LifeTimeData is also available via the regular ReadString Command because when you access LifeTimeData via registers 0x60 - 0x65 you get 6 separate returns via 32 Byte Max Block Reads. See the data sheet showing how 0x60 Returns the First Block of LifeTimeDataBlock1 that is available via Manufacturer_Access and Block Access. Register 0x61 Returns 2nd Block of Data from LifeTimeDataBlock 1.

See the data sheet saying this below.



The datasheet skips over the 0x64 & 0X65 register reads which is not right and looks like an error. I'm asking TI Support to clear this up.

This part of the TI Fuel Gauge Datasheet actually leaves out register 0x65 although its listed in the main list of SBS Commands. See below:


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

So I logged the communication between the Micro Controller <> Fuel Gauge Chip to see how many bytes registers 0x60 - 0x65 actually returned.

I then created a spreadsheet that shows the returned data and then tried to map it to the LifeTime Data Flash Summary Table, specifically the Lifetime Class.


By paying attention to the Data Address locations I found out that there is a Byte that is Reserved and not used between Cell 15 Max Voltage which is held at Address 0x40DE and then the next line is Cell 1 Min Voltage at Address 0x40D0.

So data point 0x40DF is skipped in this spreadsheet.

So here is the bytes returned when I read register 0x60


So if I include that Data at address 0x40DF and that is skipped then the data returned when I read register 0x60 comes back correctly.

Bottom line is that I need to verify with TI what data is supposed to be returned from the 0x60 - 0x65 registers because the Datasheet seems to be all over the place and is missing info.

Once I have this figured out I'll proceed to the code you have provided that will allow me to pull bytes from the returned array and then display it how ever is necessary.

Not sure if all that made sense but thanks for everything and looks like I'm almost done.

Two bytes 0x40CF and 0x40DF are missing in the table.

Yea Hopefully TI can clear up what each data point returned actually referrers to.

@pra @ScruffR

I've been getting the code all cleaned up and arranged the way I want it which has been going fine.

The fuel gauge chip has a few different modes for programming called Unsealed or Unsealed Full Access. I have been operating the fuel gauge in Unsealed Full Access mode. I flipped the Sealed button in the PC software and which put the fuel gauge in Sealed status which limits access to some of the registers I was successful pulling data from using the regular 0x54, 0x55 type SMBus commands.

Most of the data still works just fine in Sealed Mode but some of it requires I access it via manufacturer Access Commands which requires the following:

"Also available via ManufacturerAccess in sealed mode are some of the extended SBS commands. The commands available are listed below. The result of these commands need to be read from ManufacturerAccess after a write to ManufacturerAccess."

0x0050 = SBS:SafetyAlert(0x50)
0x0051 = SBS:SafetyStatus(0x51)
0x0052 = SBS:PFAlert(0x52)
0x0053 = SBS:PFStatus(0x53)
0x0054 = SBS:OperationStatus(0x54)
0x0055 = SBS:ChargingStatus(0x55)
0x0057 = SBS:ResetData(0x57)
0x0058 = SBS:WDResetData(0x58)
0x005a = SBS:PackVoltage(0x5a)
0x005d = SBS:AverageVoltage(0x5d)

I remember @pra talking about some of these read commands being difficult to work with.


I'm using this function to read the registers just fine in Unsealed Mode but it sounds like I need to do the above to read them once the chip it placed into Sealed mode after programming.

uint32_t BQ20Z45::read32u(uint8_t address)
{
	uint32_t registerValue;
      Wire.beginTransmission(BQ20Z45_Address);
	Wire.write(address);
	Wire.endTransmission(false);
	Wire.requestFrom(BQ20Z45_Address,5,true);
	    Wire.read();
        registerValue = Wire.read();
        registerValue |= (Wire.read()<<8);
        registerValue |= (Wire.read() << 16);
        registerValue |= (Wire.read() << 24);
		
        return registerValue;
}

I have these definitions at the top of my .H file, I'm assuming I need to write to the ManAccess Address instead of the normal Address when trying to do as suggested by TI above.

#define BQ20Z45_Address           0x0B

#define BQ20Z45_ManAccess         0x00

@pra is this the funky stuff you were saying is a pain earlier?

@RWB
Manufacturer_Access() and Manufacturer_Block_Access() are 5.5.6 Process Call and 5.5.8 Block write-block read process call, defined in the SMBus Specification Version 2.0. I suggest you go to smbus.org and download this spec. Basically for Manufacturer_Access() it is a 3 byte write:

  • 0x0 - Manufacturer_Access() function address
  • 0xNN - Low byte of function desired (0x50) for Safety Alert
  • 0x0 - High byte of function normally 0

followed by a restart and a 2 byte read.

5.5.6 doesn’t state whether a block read of more than 2 bytes is possible with manufacturer_access(). Don’t know whether there will be a block read response or not. You may need to use manufacturer_block_access() for them. I just don’t know.

To send the same function with Manufactuer_Block_Access() you will need to send a length byte (0x2) prior to the 2 function bytes (adde, len, low function byte, high function byte).

You are still limited to a maximum of 32 bytes on a read. That includes the length byte, so a maximum of 31 data bytes. 5.5.8 indicates a manufacturer_block_access()'s combined write and read data <= 32 bytes, so the max data payload read would be <= 30 bytes.

@pra Thanks for sticking with me :smiley:

I came across this info on TI’s Support Forum which is somebody else who is asking a question about exactly the same thing were talking about here. He gives the code structure that works and says both read options are working. See below:

These are the commands I’m interested in getting info from. All Block Data.



I’m going to try to change my read code to emulate what the guy above is doing.

Let me know if that screenshot helps out any.

@pra

OK, so I played around with the read block function trying to get as close to the code the guy in the TI forum post had used.

Let me know if you see anything that needs altered. Here is the function code:

// pass a pointer to a char[] that can take up to 33 chars
// will return the length of the string received
int BQ20Z45::readStringB(uint8_t address, char* result)
{
	int pos = 0;
	int len;

        // Read the length of the string
	Wire.beginTransmission(BQ20Z45_Address);
	Wire.write(0x44);
	Wire.write(0x02);
	Wire.write(0x50);
	Wire.write(0x00);
	
	Wire.endTransmission(false);
	Wire.requestFrom(BQ20Z45_ManBlockAccess, 1, false);
	len = Wire.read();    // length of the string
        len++;            // plus one to allow for the length byte on the reread
                          // if len > 32 then the it will be truncated to 32 by requestFrom

        // Now that we know the length, repeat the read to get all the string data. 
        // we need to write the address again and do a restart so its a valid SMBus transaction
	Wire.beginTransmission(BQ20Z45_Address);
	Wire.write(address);
	Wire.endTransmission(false);
	len = Wire.requestFrom(BQ20Z45_Address, len, true);    // readRequest returns # bytes actually read

        len--;                                             // we won't move the first byte as its not part of the string
	if (len > 0)
	{
                Wire.read();
		for (pos = 0; pos < len; pos++)
			result[pos] = Wire.read();
	}
	result[pos] = '\0';  // append the zero terminator
	
	return len;
}

Here is the LA capture when I try to read the using this function. The first 5 lines look right but not after that.

  Setup Write to [0x16] + ACK
    0x44 + ACK
    0x02 + ACK
    0x50 + ACK
    0x00 + ACK
    Setup Read to [0x88] + NAK
    Setup Write to [0x16] + ACK
    0x60 + NAK
    Setup Read to [0x16] + ACK
    0x02 + ACK
    0x00 + ACK
    0x50 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + ACK
    0x64 + NAK

What do I need to tweak to get it running?

@RWB
As you have seen that won’t work. Try this:


// pass a pointer to a char[] that can take up to 33 chars
// will return the length of the string received
int BQ20Z45::readStringB(uint8_t address, char* result, int len)
{
	int pos = 0;
        int newlen;

        // Initiate a manufacturer_block_Read
	Wire.beginTransmission(BQ20Z45_Address);
	Wire.write(0x44);
	Wire.write(0x02);
	Wire.write(0x50);
	Wire.write(0x00);

	Wire.endTransmission(false);
	newlen = Wire.requestFrom(BQ20Z45_Address, len + 1,true);
	if (newlen > 0)
	{
                newlen--;
                Wire.read();
		for (pos = 0; pos < newlen; pos++)
			result[pos] = Wire.read();
	}
	result[pos] = '\0';  // append the zero terminator

	return newlen;
}

You no longer can read the returned length by itself, so you need to pass the length as a parameter. (0 < len <= 31) based on your understanding of the spec on what you are accessing.

@ScruffR - Didn’t mean to drop you out of the discussion :smile:

3 Likes

@pra @ScruffR

Ok I successfully copied over your version of the code. Thank you very much.

I get the following Compile Errors:

I'm pretty sure its because I need to do the rest of what you are telling me here:

You no longer can read the returned length by itself, so you need to pass the length as a parameter. (0 < len <= 31) based on your understanding of the spec on what you are accessing.

So the compile error says we have issues here in the CPP file on line 153:

Here is line 153 in the .cpp file:

The next compile error line is:

Here is what I have on line 150 in the H file:

I tried adding ,int after the char* result to the code on line 150 of the .h file but it didn't fix anything.

I think what your telling me is that I can't use what the fuel gauge returns as the read byte length any longer and I need to manually supply the expected read length so it reads the correct amount of bytes and not too many. This is fine, I can figure out the byte read length and manually enter it, but I'm not sure where this is supposed to be placed.

Lets say we want to register 0x0050, the data sheet says it returns 4 byes. How to do integrate that into your latest manufacturer block read code?

@Pra @ScruffR you have been a lifesaver helping me with this

@RWB
Ryan, I think you need to start engaging your brain here. It should be obvious that if you change the parameters on a class method, as I did in my sample code, you also need to change the parameters on the method prototype AND on all the calls to that method in your code.

What do you think my narrative and the new length parameter is for? When you add that to your calls to this method, wouldn’t that be what you need to set to specify how many data bytes you want.

Also, for general purpose use, the Wire.write(0x50) should be Wire.write(address) then you will be able to use the same method to read any manufacturer_block_access() data. Did you downl;pad and read 5.5.6 and 5.5.8 in the SMBus spec?

1 Like

Please don't get upset with me if possible. I'm not trying to be lazy, although it probably looks like it.

You posted the above response 11 hours ago and ever since then I've been watching Youtube videos and reading articles on Functions and Classes and finally I was able to wrap my head around the fact that the function call in my main loop is actually causing 2 separate function calls to return the correct data. I learnt quite a few other important things along the way also that helps me understand more of what your telling me.

So I was able to make the changes and get the code to compile so I've moved one step forward today.

The block read is still not working correctly though. I'm not getting good data back and I'm not seeing the same Data transaction log as the guy above is who said returned info to him as expected. Here is what his Manufacturer_Block_Read function communication looks like:

I'm pretty sure the 0x60 & 0x76 bytes are PEC which I have turned off.

Here is your code I'm using for the the Manufacturer_read_block function:

int BQ20Z45::readStringB(uint8_t address, char* result, int len)
{
	int pos = 0;
	int newlen;

        // Initiate a manufacturer_block_Read
	Wire.beginTransmission(BQ20Z45_Address);
	Wire.write(0x44);
	Wire.write(0x02);
	Wire.write(address);
	Wire.write(0x00);
	
	Wire.endTransmission(false);
	 newlen = Wire.requestFrom(BQ20Z45_Address, len + 1,true);
	Wire.read();
	if (newlen > 0)
	{
                newlen--;
                Wire.read();
		for (pos = 0; pos < newlen; pos++)
			result[pos] = Wire.read();
	}
	result[pos] = '\0';  // append the zero terminator

	return newlen;
}

Here is the response I get from the code above.

I see the 0x44 is missing from my data exchange compared to what was reported to be working for this guy:

How is he getting 0x44 after the 0x16? which looks like it triggers the return of the expected return byte count?

Yea got it and I made the suggested change. That keeps the code smaller.

Yes I did. I printed it out and its sitting right here in front of me while I look at the guys code and try to change code to try to get that 0x44 address included after 0x16 read on the read process.

@pra Any ideas? I'm gonna keep trying.

@RWB
Can you post the LA graph when you do this. The only reason I can think of that the 0x44 is not returned is because the read is somehow not a restart - it’s a new transaction (i.e. the proceeding Wire.endTransission was true, instead of false.

Once we get it to work correctly, we need to look at how to process the results:

0    -    0x44       manufacturer block access function
1    -    0x17      Unknown
2    -    0x06      data length 6 bytes
3    -    0x50      Data designator read low byte (Safety Alert)
4    -    0x00      Data designator read high byte
5    -    0x00      Date bits 7-0
6    -    0x00      Data bits 15 - 8
7    -    0x00      Data bits 23 - 16
8    -    0x00      Data bits 31 - 24

So we will have 5 bytes of header data before the payload, not 1
Peter

Just out of curiosity, what was the calling statement for readStringB() in particular the len parameter for the above result?