Go Down

Topic: Issues with changing the slave address of pressure sensors (Read 292 times) previous topic - next topic

Max_Merker

Hello guys,

I am trying to change the slave address of a NPA 201 pressure sensor by Amphenol Sensors. I want to connect up to 50 sensors with the Arduino UNO R3 and according to the data sheet, it is possible to change the address by modifying the sensor's EEPROM. Right now I am working with the NPA 201 EV-Board which has the I2C pull-ups included. The procedure for the address change is explained in the application note attached below, however, I had some problems sending the commands via the Wire.h library. Somehow I managed to manipulate the wrong data bits in the EEPROM and already destroyed one sensor by trying to change its address :( I can't find the sensor with the i2c_scanner anymore...

A possible issue could be that I could not send the commands in step 2 (see Application Note) properly. I checked some of the Wire.h-commands on an oscilloscope, and I can't figure out how to realize a command like that one:

[7 bit address + Read bit (=1)] , [command byte ( = 0x20 )]

Whenever I use "Wire.requestFrom(sensor_address);" it doesn't seem to accept any other command bytes. It is also possible that all the commands worked properly and that I just modified the wrong bits in the memory.
I also tried out the Soft.I2C library but couldn't get it done yet.

Do you guys have any idea how to send commands like that with the Wire.h library or could you think of any other possibilities? I'd prefer not to use a multiplexer, because space is limited in my application.

I'll attach my code below (the one that killed the sensor), maybe there are some other mistakes in it which I did not seeā€¦

I am grateful for any kind of help and suggestions, thanks for your time! :)

Cheers, Max


Code: [Select]

/* NPA 201
25.07.2017
*/

#include <Wire.h>

const int sensor_addr    = 0x27;   //default sensor address
const int read_delay    = 21;       //read delay suggested in datasheet
const int power_delay   = 1000;     //delay before powering on the sensor
int PowerPin            = 13;
int rbuf[5], msb[17], lsb[17];        //msb und lsb for memory read, rbuf for pressure reading
float P, T, Pconv, Tconv ;
byte error;

void setup() {
  pinMode(PowerPin, OUTPUT);
 
  Wire.begin();           // join i2c bus
  Serial.begin(9600);     // start serial communication
}

void loop() {

  // NPA201 barometric pressure sensor

  //power on sensor (the sensor needs to be set in command mode right after start-up
  delay(power_delay);
  digitalWrite(PowerPin, HIGH);
  delay(1);                               // necessary delay for sensor probably due to start-up time

  // put sensor in command mode
  Wire.beginTransmission(sensor_addr);    // transmit to device
  Wire.write(0xA9);                       // start command mode
  error = Wire.endTransmission();         // stop transmitting

  delay(read_delay);
  if (error == 0) {
    Serial.println("transmission 1 successful");
  }
  else {
    Serial.println("error in transmission 1");
  }




  // Read data from sensor memory
  for (int i = 20; i <= 36; i++) {          //assuming decimal counting here, maybe that was a first issue
    Serial.print("Location ");
    Serial.println(i);
   
    Wire.beginTransmission(sensor_addr);    // transmit to device
    Wire.write(i);                          // move to each register location. That was my workaround trying to read out the memory data
    Wire.endTransmission();                 // stop transmitting
    delay(read_delay);

    //read register data and output to serial console
    Wire.requestFrom(sensor_addr, 3);       // request 3 bytes from sensor
    Serial.print("Statusbyte: ");
    Serial.println(Wire.read(),BIN);
    Serial.print("MSB: ");
    msb[i] = Wire.read();
    lsb[i] = Wire.read();
    Serial.println(msb[i],BIN);
    Serial.print("LSB: ");
    Serial.println(lsb[i],BIN);
   

  }                   
    //MODIFY ADDRESS DETAILS

    lsb[22] = 0x28;                        //Word02 (Location 22) should contain address details
    Serial.println("new address: ");
    Serial.print(lsb[22], BIN);



    // increment sensor memory page counter

    Wire.beginTransmission(sensor_addr);
    Wire.write(0x5E);                        // increment memory page
    error = Wire.endTransmission();         // stop transmitting
 
    delay(read_delay);
      if (error == 0) {
      Serial.println("sensor incrementation successful");
    }
     else {
    Serial.println("error in transmission (step 4)");
  }



    //Write new memory page to sensor
int k = 20;                                                       // k as counter to access previously saved register values

  for (int j = 40; j<=56; j++) {
   
    Wire.beginTransmission(sensor_addr);                          // transmit to device
    Wire.write(j);Wire.write(msb[k]);Wire.write(lsb[k]);          // send command bytes to memory
    Wire.endTransmission();                                       // stop transmitting
    delay(read_delay);

    k = k+1;

  }                       // end of writing loop



    // create memory page checksum

  Wire.beginTransmission(sensor_addr);    // transmit to device
  Wire.write(0xAA);                       // checksum command
  error = Wire.endTransmission();         // stop transmitting
 
  delay(read_delay);
  if (error == 0) {
    Serial.println("checksum  successful");
  }
  else {
    Serial.println("error in transmission (step 6)");
  }
   




  //exit programming mode
  Wire.beginTransmission(sensor_addr);    // transmit to device
  Wire.write(0xA8);                       // end command mode
  error = Wire.endTransmission();         // stop transmitting
 
  delay(read_delay);
  if (error == 0) {
    Serial.println("Exit Programming mode successful");
  }
  else {
    Serial.println("error in transmission (step 7)");
  }
 

  // check status
  Wire.requestFrom(sensor_addr, 1);
  Serial.print("Statusbyte: ");
  Serial.println(Wire.read(),BIN);




  //----------------------------------------------------------------------
  digitalWrite(PowerPin, LOW);
  Serial.println("Sensor off");


 
 
  delay(60000);                  // delay in msec

}


pylon

If we take the AN seriously (I hope we can), you cannot use the Wire library to do this as it follows the I2C standard and this procedure breaks with the standard. It expects another byte to be sent in a read request after the address byte and read bit is sent before the slave has to answer. I never saw such a procedure before. As far as I can see the Arduino I2C hardware isn't able to handle that process. That means you probably have to program that out using a bitbang software emulation.

chucktodd

#2
Aug 01, 2017, 12:09 am Last Edit: Aug 01, 2017, 12:24 am by chucktodd Reason: timeouts
If we take the AN seriously (I hope we can), you cannot use the Wire library to do this as it follows the I2C standard and this procedure breaks with the standard. It expects another byte to be sent in a read request after the address byte and read bit is sent before the slave has to answer. I never saw such a procedure before. As far as I can see the Arduino I2C hardware isn't able to handle that process. That means you probably have to program that out using a bitbang software emulation.
I disagree,

I decode the Programing sequence as:
Code: [Select]

uint8_t sensorOldAddress=0x27;  // whatever the 'current' address is
uint8_t sensorNewAddress; // whatever the address is to be changed to.

// with the appnote, I CANNOT tell where there are 17(decimal) or 0x17(hex memory cells?)
// I am assuming 0x17(HEX)

// Also, I cannot tell whether XX=20 means XX=0x20 or XX=20  (big Difference!!!!)

uint8_t buffer[0x17][4]; // temp storage of EEPROM contents of sensor
bool success=true;

Wire.beginTransmission(sensorOldAddress);
Wire.write(0xA9); // start command mode
Wire.endTransmission();

// read all 23 EEPROM words
uint8_t dataWord=0; // start a 0
while((dataWord<0x17)&&(success)){
  uint8_t err=Wire.requestFrom(sensorOldAddress),4);
  if (err==4){
    buffer[dataWord][0]=Wire.read();
    buffer[dataWord][1]=Wire.read();
    buffer[dataWord][2]=Wire.read();
    buffer[dataWord][3]=Wire.read();
    }
  else {
     Serial.println("Something Bad Happend");
     success=false;
    }
  dataWord++;
  }
if(success){

  // printout values
  dataWord=0;
  Serial.println("EEPROM Contents");
  while(dataWord<0x17){
    Serial.print(dataWord,HEX);
    Serial.print(": ");
    Serial.print(buffer[dataWord][0],HEX);
    Serial.print(' ');
    Serial.print(buffer[dataWord][1],HEX);
    Serial.print(' ');
    Serial.print(buffer[dataWord][2],HEX);
    Serial.print(' ');
    Serial.println(buffer[dataWord][3],HEX);
    dataWord++;
    }

  if(false){ // change address, put some decision logic here!

    // sent new address

    buffer[2][3]=(buffer[2][3]&0x80)|sensorNewAddress;

  // increment to next config storage location (only 3 available)

    Wire.beginTransmission(sensorOldAddress);
    Wire.write(0x5E);
    Wire.endTransmission();

    dataWord=0;
    while((dataWord<0x17)&&success){
      Wire.beginTransmission(sensorOldAddress);
      Wire.write(dataWord+0x40);
      Wire.write(buffer[dataWord][2]);
      Wire.write(buffer[dataWord][3]);
      uint8_t err=Wire.endTransmission();
      if(err!=0){
        success=false;
        }
      else dataWord++;
     }

// have sensor calculate new checksum

    Wire.beginTransmission(sensorOldAddress);
    Wire.write(0xAA);
    Wire.endTransmission();
    }
// Ext Program mode

  Wire.beginTransmission(sensorOldAddress);
  Wire.Write(0xA8);
  Wire.endTransmission();
  }

// all done.
// power Cycle Sensor, it should now respond to new address



EDIT: add
This code has no ready/busy detection, the appnote did not specify any, but the datasheet does?

I would probably insert a call to:
Code: [Select]

bool waitReadyI2c(uint8_t address){ // wait up to 1 second for device
bool ready=false;
unsigned long timeout=millis();
while((millis()-timeout<1000)&&(!ready)){
  Wire.beginTransmission(address);
  ready = Wire.endTransmission()==0;
  }
return ready;


before writing each of the 'words', fail if not ready

Chuck.
Currently built mega http server, Now converting it to ESP32.

el_supremo

Did you try the search function of this forum?
This thread might have useful info.

Pete
Don't send me technical questions via Private Message.

Max_Merker

Yeee, thanks for the quick replies!

@Pete: Yeah I saw that post, but I couldn't get much more out of it than the regular pressure/temperature reading procedure (which works perfectly fine).

@Chuck: Thanks for the code, I'll try to work with the suggestions! By now I finally got an answer from Amphenol, the data words are in HEX, so resulting in 23 Words from 0x00 to 0x16.

Max

pylon

Quote
I disagree,
In this case table 1 on page 2 is completely misleading! On step 2 you have byte 1 the address and read bit, byte 2 is the memory address. This byte is marked as "Send" and not "Return".

chucktodd

In this case table 1 on page 2 is completely misleading! On step 2 you have byte 1 the address and read bit, byte 2 is the memory address. This byte is marked as "Send" and not "Return".
Ya, that Datasheet created more questions than it answered.

Chuck.
Currently built mega http server, Now converting it to ESP32.

Max_Merker

So just in case anyone might have the same problem in the future:

My code still doesn't work as it should, but I found a workaround. There were some errors in the Application Note (I got this confirmed by the Amphenol customer support team). For example the Words to be read from the memory are from Location 0x00 to 0x16, the low byte of Word 0x02 is the slave address. After having redefined the address byte, all the words are written to the memory locations 0x40 to 0x56.

Anyways, there's still an error in the procedure, maybe the page incrementation does not work properly.

A working solution is to skip the page incrementation and just change the slave address while remaining on the same page. This way you're limited in the choice of addresses, as you can only set the "0"-bits in the address byte. Starting from 0x27 you can change the address to 0x2F, 0x37, 0x3F, 0x67, 0x6F and 0x77.

Surprisingly, this works fine.
I got lucky as I got only early production samples with my latest order of new sensors. Early production samples have the address 0x00 by default, so I was able to set any address I wanted.


Cheers, Max

Code: [Select]

/* NPA 201
 * Code for changing the slave address while staying on the same memory page
30.08.2017
*/

#include <Wire.h>

const int sensor_addr    = 0x00;   //current sensor address

const uint8_t read_delay    = 30;       //read delay suggested in datasheet
const uint16_t power_delay   = 1000;     //delay before powering on the sensor

uint8_t PowerPin            = 13;       //PIN powering the sensor, don't forget to include resistors to shift it to 3 - 3.3V!!!

uint8_t error;

const uint8_t memoryCount = 23;
uint8_t byteLow[memoryCount];
uint8_t byteHigh[memoryCount];

void checkPageNumber()
{
  static const int count = 3;

 
  Wire.beginTransmission(sensor_addr);
  Wire.write(0x20);
  Wire.endTransmission();
  delay(read_delay);
  Wire.requestFrom(sensor_addr, count);

  Wire.read();
  uint8_t value = Wire.read();
  Wire.read();
  Serial.println(value, BIN);
  if (value & 0x20 && !(value & 0x40))
  {
    Serial.println("Page 1");
  }
  else if (value & 0x40 && !(value & 0x20))
  {
    Serial.println("Page 2");
  }
  else if (value & 0x60)
  {
    Serial.println("Page 3");
  }
  else
  {
    Serial.println("Page 0");
  }

 
}


void checkState()
{
  Wire.beginTransmission(sensor_addr);
  Wire.endTransmission();
  delay(read_delay);
  Wire.requestFrom(sensor_addr, 1);
  uint8_t state = Wire.read();
  Serial.print("State: 0x");
  Serial.println(state, HEX);
}


void createChecksum()
{
  Wire.beginTransmission(sensor_addr);
  Wire.write(0xAA);
  error = Wire.endTransmission();
  delay(read_delay);
  if (error == 0){
    Serial.println("Checksum created successful");
  }
  else {
    Serial.println("Error in Transmission (checksum)");
  }
}

void exitCommandMode()
{
  Wire.beginTransmission(sensor_addr);
  Wire.write(0xA8);
  error = Wire.endTransmission();
  delay(read_delay);
  if (error == 0) {
    Serial.println("Exit Programming mode successful");
  }
  else {
    Serial.println("error in transmission");
  }
}


void incrementPageNumber()
{
  Wire.beginTransmission(sensor_addr);
  Wire.write(0x5E);
  error = Wire.endTransmission();
  delay(read_delay);
  if (error == 0) {
    Serial.println("page number incrementation successful");
  }
  else {
    Serial.println("Error in Transmission (Increment Page Number)");
  }
}


void readMemory()
{
  for (uint8_t i = 0; i < memoryCount; i++)
  {
    Wire.beginTransmission(sensor_addr);
    Wire.write(i);
    Wire.endTransmission();
    delay(read_delay);
    Wire.requestFrom(sensor_addr, 3);
    uint8_t state = Wire.read();
    byteHigh[i] = Wire.read();
    byteLow[i] = Wire.read();
    Serial.print("State: 0x");
    Serial.println(state, HEX);
    Serial.print("Memory ");
    Serial.println(i);
    Serial.print("High Byte (15:8): 0x");
    Serial.println(byteHigh[i], HEX);
    Serial.print("Low Byte (7:0): 0x");
    Serial.println(byteLow[i], HEX);
  }
}


void setNewAddress()
{
  bitSet(byteLow[2], 0);                   
  bitSet(byteLow[2], 1);
  bitSet(byteLow[2], 2);
//  bitSet(byteLow[2], 3);
  bitSet(byteLow[2], 4);
  bitSet(byteLow[2], 5);
 
  Serial.println("new value for Word 02 (address byte) 0x..: ");
  Serial.println(byteHigh[2],HEX);
  Serial.println(byteLow[2],HEX);
}

void setToCommandMode()
{
  Wire.beginTransmission(sensor_addr);
  Wire.write(0xA9);
  error = Wire.endTransmission();
  delay(read_delay);
  if (error == 0) {
    Serial.println("entering command mode successful");
  }
  else {
    Serial.println("Error in Transmission (enter command mode)");
  }
}


void writeMemory()
{
  Wire.beginTransmission(sensor_addr);
  Wire.write(0x42);                  //move to Word 0x02
  Wire.write(byteHigh[2]);              //0x00
  Wire.write(byteLow[2]);               //new slave address
  error = Wire.endTransmission();
  delay(read_delay);
  if (error == 0) {
    Serial.println("write memory successful");
  }
  else {
    Serial.println("Error in Transmission (write Memory)");
  }
}


void setup() {
  pinMode(PowerPin, OUTPUT);
 
  Wire.begin();           // join i2c bus
  Serial.begin(9600);     // start serial communication


  //power on sensor (the sensor needs to be set in command mode right after start-up
  delay(power_delay);
  digitalWrite(PowerPin, HIGH);
  delay(1);                               // necessary delay for sensor probably due to start-up time

  setToCommandMode();

  checkState();

  checkPageNumber();

  readMemory();
 
// bit to set for new address
  setNewAddress();

//  WRITING starts here: 
  writeMemory();

//leave out checksum creation, it doesn't work with this procedure!
//  createChecksum();


  exitCommandMode();

  //----------------------------------------------------------------------
  digitalWrite(PowerPin, LOW);
  Serial.println("Sensor off");


}

void loop() {

}


Go Up