A capacitive soil humidity sensor: Part 2

capacitive74HC14_attinyIn a previous article I presented a simple way to read a capacitive moisture sensor with a simple RC generator.
In this post I will present a sensor with some added functionality that can be read through I2C. The circuit (figure) doesnt need much explanation: the RC generator we saw in the previous article and the two variable resistors in a voltage divide read by analogue inputs. The Attiny 45 (NOTE, it is NOT an Attiny25) is the heart, or rather the brains of the sensor. As the Attiny  functions as an I2C slave we will need the TinyWireS library. The library comes with some examples and one example was quite easy to rework to what I needed. The code is as follows.

#define I2C_SLAVE_ADDRES 0x4
#include <TinyWireS.h>//https://github.com/rambo/TinyWire
#ifndef TWI_RX_BUFFER_SIZE
#define TWI_RX_BUFFER_SIZE ( 16 )
#endif

volatile uint8_t i2c_regs[] =
{
	0x00,
	0x00,
	0x00,
	0x00,
};
// Tracks the current register pointer position
volatile byte reg_position;
const byte reg_size = sizeof(i2c_regs);

byte LDRvalue;
byte NTCvalue;
int MoistPulse;

/**
 * This is called for each read request we receive, never put more than one byte of data (with TinyWireS.send) to the
 * send-buffer when using this callback
 */
void requestEvent()
{
	TinyWireS.send(i2c_regs[reg_position]);
	// Increment the reg position on each read, and loop back to zero
	reg_position++;
	if (reg_position >= reg_size)
	{
		reg_position = 0;
	}
}

/**
 * The I2C data received -handler
 *
 * This needs to complete before the next incoming transaction (start, data, restart/stop) on the bus does
 * so be quick, set flags for long running tasks to be called from the mainloop instead of running them directly,
 */
void receiveEvent(uint8_t howMany)
{
	if (howMany < 1) { // Sanity-check return; } if (howMany > TWI_RX_BUFFER_SIZE)
	{
		// Also insane number
		return;
	}

	reg_position = TinyWireS.receive();
	howMany--;
	if (!howMany)
	{
		// This write was only to set the buffer for next read
		return;
	}
	while(howMany--)
	{
		i2c_regs[reg_position] = TinyWireS.receive();
		reg_position++;
		if (reg_position >= reg_size)
		{
			reg_position = 0;
		}
	}
}


void setup()
{
	pinMode(1, INPUT);
	TinyWireS.begin(I2C_SLAVE_ADDRESS);
	TinyWireS.onReceive(receiveEvent);
	TinyWireS.onRequest(requestEvent);
}

void loop()
{
	readSensors();
	/**
	 * This is the only way we can detect stop condition (http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=984716&sid=82e9dc7299a8243b86cf7969dd41b5b5#984716)
	 * it needs to be called in a very tight loop in order not to miss any (REMINDER: Do *not* use delay() anywhere, use tws_delay() instead).
	 * It will call the function registered via TinyWireS.onReceive(); if there is data in the buffer on stop.
	 */
	TinyWireS_stop_check();
}

void readSensors()
{
	LDRvalue = (analogRead(A2)) >>2; // max value is 1023/4=255 is 1 byte (FF) physical pin3=PB4=A2
        i2c_regs[0] = LDRvalue;
	NTCvalue = (analogRead(A3)) >>2; // max value is 1023/4=255 is 1 byte (FF) pin2=PB3=A3
	i2c_regs[1] = NTCvalue;
	//pulsepin= pin6=PB1
	MoistPulse = pulseIn(1, HIGH);
	i2c_regs[2] = highByte(MoistPulse); // or use = MoistPulse >> 8;
	i2c_regs[3] = lowByte(MoistPulse); // or use = MoistPulse & 0xFF;
}

I have called the pulseIn function with an integer rather than with a long. This means that you will have to choose a value for R3 that gives a reasonable range for the type of soil that you use. A range of say 0-200 uS is very reasonable. Once you have done that you can also add a timeout to the pulseIn function. This should be abt 2 times the pulselength you expect (but depends on the duty cycle). With regard to the two variable resistors, they are in pull up so their value is Rntc = Rseries/((1023/ADC) – 1));  For the NTC this could be substituted in a Steinhart Hart approximation

The code to call the values (and that is loaded onto the master arduino) is even simpler:

#include <Wire.h>
void setup() {
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop() {
  for (byte i=0;i<4;++i){
  Serial.print("0x");
  Serial.println(readRegister(i),HEX); // print the character
}
Serial.println(" ");
    delay(1000);
  }

uint8_t readRegister(uint8_t regaddress)
{
	Wire.beginTransmission(4);
	Wire.write((byte)regaddress);
	Wire.endTransmission();
	Wire.requestFrom(4, 1);
	return Wire.read();


 delay(500);
}

This code only prints out the values, you still need to combine the LSB and MSB from the cycle time and e.g. switch a pump based on the value. You could do that with this function:

int combine (byte lsbv, byte msbv)
{
	int value = msbv << 8;
	value = value | lsbv;
	//value= msbv<<8 | lsvb;//if you want to do it in one go
	return value;
}

Don’ t forget that the I2C lines need pull-up resistors of 4.7-10k.

In a next article I will present the construction of the probe itself.

9 thoughts on “A capacitive soil humidity sensor: Part 2

  1. I’m trying to use your code to read potentiometer value from 0-1023 in the attiny85 and send the value through i2c to arduino uno (master). but i only got FF HEX value in the arduino. So the point is, how to make attiny read adc in 10 bit on in 8 bit? Thank you for your help.

    1. well obviously you cant read 10 bits into 8 bits, but there are two possibilities for your problem. Unless you really need the 0-1023 you could map the value to 255. Although the attiny understands the MAP command, it takes a lot of memory and it is better to just divide the reading by 4. In My exampple I have done this for the NTC and the LDR
      If you really do need the 10 bits value, you need to put the LSB and MSB in two seperate 1 byte registers, read those and then in your Arduino combine them again, In my code I have done this with the moisture reading

      1. i’m completely lost and still getting 255 from the slave. i guess i need to learn a lot about the register in a microcontroller.

      2. My code works but you use it for a different project. My code does read an entire integer for moisture. What happens if you use my code and the Moisture registers for your potentiometer.
        You will learn. it was my first I2C slaveproject as well

  2. Hi, i found this series of articles interesting, thanks for them. But, please consider to link them from one to other, it is terrible to find other parts…

    1. Thank you, but I am not really sure what you mean about the linking.I thought I did. Do you have an example? I might have overlooked from time to time

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s