After three weeks of banging my head against a brick wall, I confirmed last night that the AS3935 has an issue with the I2C interface. There is an issue with the chip that prevents reading register zero without violating the I2C protocol. Interestingly, once you read it the first time, it works properly for the rest of that power cycle. I have confirmed this on the Embedded Adventures and Tautic Electronics (in I2C mode).
According to the I2C spec, to read register 0 you need a few bus cycles.
1) Master sends over the device address with the read bit cleared to indicate a write. The AS3935 then must ACK this transaction.
2) Master then sends over the register value (0 in this case) and the AS3935 must ACK.
3) Next the bus is reserves with a repeated stop performed by the master.
4) The master now sends the device ID with the read bit set. The AS3935 must ACK.
5) Now the master is free to read the value by clocking the data from the AS3935. At the end the AS3935 will ACK or NAK based on how many bytes are to be read.
The issue is that at step 2, the AS3935 NAKs the transaction. A hardware I2C peripheral will assume a bus failure and abort further transactions. If one ignores the error (a bad practice), you can continue on and clock the data out of register 0.
There are several workarounds that I have found. None are very great, but can make get the chip into a useful state.
a) Don’t ever read or write register 0. This works if you do not need to change the gain or put the chip into power-down mode.
b) Do not use a hardware I2C peripheral to perform the bus transaction but rather bit-bang the interface. This solution is ugly and slow as the CPU needs to be involved at all times unlike the peripheral that is interrupt driven. Bit banging the interface also increases power consumption as the micro can’t be in a low power mode during transfers.
c) Rewrite vendor supplied interrupt driven libraries that use the hardware I2C peripheral to ignore the error. This is the wrong choice for way to many reasons.
d) Use a hybrid approach. Using a bit-bang approach on power up, read register 0. Then abandon the bit-bang and use the hardware I2C peripheral from then on. This is the best solution, but suffers from code bloat. You need twice the code to do the same action.
I have elected to use option D. I will be rewriting the application code over the weekend to add bit-banging for the first read. Then I need to place an order for an Arduino mini and an X-Bee to allow me to place the detector in the attic where it will have a better ‘view’ of incoming storms.
Edit: I got an email back from AMS indicating that this is indeed an issue. But they also mention it is only a problem if you strap the board for device address of 0. This is not a problem if you use a different device ID.
Having done quite a bit of work with i2c I would never use an address of 0 as this was always intended as a universal address which any number of different devices will respond to. If you have an i2c bus with multiple devices attached and several attempt to respond to address 0 at the same time results can get very unpredictable. It tends to be used for devices where the address can be programmed by software to allow multiple devices of the same type on a single bus.
I would aggree, but the chip only supports four address using a pin configuration method. The vendor of the breakout board picked address 0 on the first revision of the board. They now picked a different address for the boards they are shipping now.