PSLab device is a compact electronic device with a variety of features. One of them is the ability to integrate sensors and get readings from them. One might think that why they should use PSLab device to get sensor readings and they can use some other hardware like Arduino. In those devices, user has to create firmware and need to know how to interface them with correct sampling rates and settings. But with PSLab, they all come in the whole package. User just have to plug the device to his phone and the sensor to device and he’s ready.
The idea of this blog post is to show how this sophisticated process is actually happening. Before that, let me give you a basic introduction on how I2C communication protocol works. I2C protocol is superior to UART and SPI protocols as they are device limited or requires many wires. But with I2C, you can literally connect thousands of sensors with just 4 wires. These wires are labeled as follows;
- VCC – Power line
- GND – Ground line
- SDA – Data line
- SCL – Signal clock
It is required that the SDA and SCL lines need to be connected to VCC line using two pull up resistors. But that’s just hardware. Let’s move on to learn how I2C communicate.
Here there is this communicating concept called master and slave. To start communication, master issues a global signal like a broadcast to all the devices connected to SCL and SDA lines. This signal contains the address of the slave, master needs to address and get data from. If the slave received this call to him, he will pull down the SDA line to signal the master that he heard him and ready to communicate with him. Here communication means reading or writing data. Then the communication happens and the link between master and slave breaks giving opportunity to other masters and slaves.
One might think this is a slow process. But these signals are transmitted at high frequencies. In PSLab it is at 100 kHz and that is one millisecond.
PSLab library has a special class to handle I2C communication. That is
public class I2C {/**/}
Once this class is initiated, one has to call the start function to start communication. This method requires the address we wish to communicate with and the mode of operation stating if it is a read or write operation
public int start(int address, int rw) throws IOException { packetHandler.sendByte(commandsProto.I2C_HEADER); packetHandler.sendByte(commandsProto.I2C_START); packetHandler.sendByte((address << 1) | rw & 0xff); return (packetHandler.getAcknowledgement() >> 4); }
Once the address is sent out, protocol requires us to stop and wait for acknowledgement.
public void wait() throws IOException { packetHandler.sendByte(commandsProto.I2C_HEADER); packetHandler.sendByte(commandsProto.I2C_WAIT); packetHandler.getAcknowledgement(); }
If there are no congestion in the lines such as reading from multiple devices, the acknowledgement will be instantaneous. Once that is complete, we can start communication either byte-wise or bulk-wise
public int send(int data) throws IOException { packetHandler.sendByte(commandsProto.I2C_HEADER); packetHandler.sendByte(commandsProto.I2C_SEND); packetHandler.sendByte(data); return (packetHandler.getAcknowledgement() >> 4); }
As an example, reading sensor values at a given interval can be done using the following method call.
public ArrayList<Byte> read(int length) throws IOException { ArrayList<Byte> data = new ArrayList<>(); for (int i = 0; i < length - 1; i++) { packetHandler.sendByte(commandsProto.I2C_HEADER); packetHandler.sendByte(commandsProto.I2C_READ_MORE); data.add(packetHandler.getByte()); packetHandler.getAcknowledgement(); } packetHandler.sendByte(commandsProto.I2C_HEADER); packetHandler.sendByte(commandsProto.I2C_READ_END); data.add(packetHandler.getByte()); packetHandler.getAcknowledgement(); return data; }
Once we get the data bundle, either we can save them or display in a graph whatever the way it’s convenient.
Reference: