Build Your Own I2C Sensor

Build Your Own I2C Sensor


Since Raspberry Pi doesn’t have a built-in ADC (Analog to Digital converter) to read the voltage off from most of sensors, the best solution is to add I2C ADC chips and modules to your project.

Paweł Spychalski faced this problem while building his own weather station that is based on Raspberry Pi. It collects various data and displays them on dedicated web page and Android app. Every few months he tries to add a new sensor to it. Last time it was a daylight sensor. He added this sensor to his system by using ATtiny85 and it was connected via I2C bus.

ATtiny85 is a member of Atmel tinyAVR series which has 8-bit core and fewer features, fewer I/O pins, and less memory than other AVR series.

The Inter-integrated Circuit (I2C) Protocol is a protocol intended to allow multiple “slave” digital integrated circuits (“chips”) to communicate with one or more “master” chips. Like the Serial Peripheral Interface (SPI), it is only intended for short distance communications within a single device. Like Asynchronous Serial Interfaces (such as RS-232 or UARTs), it only requires two signal wires to exchange information.

I2C uses only two bidirectional open-drain lines, Serial Data Line (SDA) and Serial Clock Line (SCL), pulled up with resistors. Typical voltages used are +5 V or +3.3 V although systems with other voltages are permitted.

Sample Inter-Integrated Circuit (I²C) schematic with one master (a microcontroller) and three slave nodes

Most of developers use I2C to connect to sensors with the help of the Arduino “Wire” library or “i2c-tools” on the Pi, but it is rare to see someone that is actually building the I2C slave device. Paweł’s project uses TinyWireS library, a slave-mode SPI and I2C library for AVR ATtiny Arduino projects.

This diagram shows how to build analog to digital converter using ATtiny85 and connect it to any device (Raspberry Pi, Arduino) using I2C bus. Here photoresistor has been used, but any analog meter will be fine: temperature, potentiometer, moisture…

ATtiny85 directly connected to Raspberry Pi via I2C, photoresistor with 10kOhm pull down connected to ATtiny85 and signal LED.

ATtiny85 directly connected to Raspberry Pi via I2C, photoresistor with 10kOhm pull down connected to ATtiny85 and signal LED.

For reading data you can use this code. ATtiny sends current measurement as two 8 bit value. First older bits, then younger 8 bits.

Wire.requestFrom(0x13, 2);    // request 2 bytes from slave device #0x13

int i =0;
unsigned int readout = 0;

while (Wire.available()) { // slave may send less than requested
 byte c =; // receive a byte as character

 if (i == 0) {
  readout = c;
 } else {
  readout = readout << 8;
  readout = readout + c;



To do this project you need to use Arduino IDE 1.6.6., TinyWireS library,ATtiny45/85 board, plus an 1MHz internal oscillator.

Watchdog timer interrupts ATtiny every few minutes, measures voltage, filters it and stores in memory. Every time read operation is requested, last filtered ADC value (10 bits as 2 bytes). I2C support is provided by TinyWireS library that configures ATtiny USI (Universal Serial Interface) as I2C slave.

* This function is executed when there is a request to read sensor
* To get data, 2 reads of 8 bits are required
* First requests send 8 older bits of 16bit unsigned int
* Second request send 8 lower bytes
* Measurement is executed when request for first batch of data is requested
void requestEvent() {

 if (reg_position >= reg_size) {
  reg_position = 0;

* Setup I2C
TinyWireS.onRequest(requestEvent); //Set I2C read event handler


Bright by day, dark by night
Bright by day, dark by night

This cool weather station and its need of daylight sensor is only an example. The amazing thing is that you can now build new I2C sensors and introduce new modules to your projects easily following Paweł’s steps.

For more details about this project you can check Github and the weather station website.

Notify of
1 Comment
Inline Feedbacks
View all comments

Hello, i used your code but i have an error.

WDTCR |= (1<<WDCE) | (1<<WDE);



exit status 1

‘WDTCR’ was not declared in this scope

Could you help me pls?