Categories
Product Review

Test Video

I would like to add more videos to the blog. One of the problems that I have had is the time it takes to perform post processing on videos to add different sources and information. I found the Open Broadcaster Software application and started messing around with it and found that it is quite good, once you get use to how it works. Here is my first attempt at creating a video with the software.

BTW: Yes, I totally ripped off some ideas from Adafruit. I’m not too creative so I copied what I like. I will attempt to change it but it is tough as Limor and Phil have created a very nice format for the shows that Adafruit produces. I also like Ben Heck and Dave Jones (EEVBlog) videos as well but I seem to watch more of the videos Adafruit puts out.

Here is my first recording.

Categories
Arduino Raspberry Pi

I2C Communications between Raspberry Pi and Arduino – Update

Over the past few days, I have been able to bit bang the I2C bus with the PIGPIO library. I found a very helpful example at http://raspberrypi.stackexchange.com/questions/3627/is-there-an-i2c-library/4052 which was posted by crj11. This example was using the pigpiod_if.h library. This worked well but required the application to be run with elevated privileges (sudo) which was not acceptable as I needed to run the application from a Python script which in turn was running every 5 minutes from a cron job. The final solution was to use the pigpiod_if2.h and run the pigpio daemon on startup.

The final solution has been running for a few days with three sensors connected over a total of 9 feet (~3 meters) of cable. The data has been logged at Adafruit.IO which I have made public. I may remove or rename the Dashboard in the near future so here is a screenshot of the page.
adafruit_io_v0.1.0

I have also updated the GitHub site for the project so there are now two releases available. Version RC_0.0.1 is the version using the hardware for I2C control and version RC_0.1.0 is the bit bang version.

I still have more work to do to make this project valuable to others. I plan to create some better documentation on the project and provide a full write-up to allow someone to follow along and build there own from start to finish. Right now, 80% to 90% is captured in various places but there are obvious gaps such as the connection to the Raspberry Pi. One can figure this missing information out by looking through the right source files and piece it together however I do not like it when I find a project write-up that is only 80% to 90% documented. It is still better than nothing or only 10% to 25% though.

I hope someone will find some of the information here useful.

Categories
Arduino Raspberry Pi

I2C Communications between Raspberry Pi and Arduino – Update, BOM, and Source Control

Update

I have been working on this project over the past couple of weeks when I have free time but have not been posting updates. This is a general update which is why the title is different from the other posts regarding this project.

My boards from OSH Park arrived last week. I was able to populate them and test them out. Fortunately I did not make any errors on the PCB or schematic so they all worked as designed. There are a few things that I would change on a future version if I choose to make another version of the board.

  1. I would put the ICSP header on the bottom of the board so it does not stick out from the front. This would make it much easier to assemble and would make it possible to not have any exposed circuitry which may allow the device to be damaged from static electricity.
  2. I would move the resistors toward the bottom of the board if possible. It would allow the DHT11 sensor to stick out further from the case.
  3. I would also try to push the ATtiny85 a little further towards the bottom for the same reason as the resistors.

Currently I am looking to bit bang the I2C bus on the Raspberry Pi. I seem to have gotten around the clock stretching issue if there is only one device connected to the I2C bus but as soon as I add another device, the clock stretching becomes an issue again. I really wish that the Pi Foundation would work with Broadcom and fix the issue with the I2C bus.

Here are some pictures.

This slideshow requires JavaScript.

Bill of Materials (BOM)

Materials List (For One Sensor)

  • QTY 1 – PCB (Source: OSH Park)
  • QTY 2 – 3.5mm 4 Pole Audio Jack, J1 & J2 (Source: MCM Electronics NOTE: I ordered 27-9485 but received ones which look like 27-9487)
  • QTY 1 – 2×3 Position Male Header 2.54mm Pitch, JP1 (Source: MCM Electronics NOTE: This is a 2×13 header. You will need to cut it for 2×3 header.)
  • QTY 1 – T-1 3mm LED, LED1 (Source: MCM Electronics NOTE: May choose another color)
  • QTY 1 – CDS Photoresistor Photoelectric 5549 GL5549, PH1 (Source: Ebay)
  • QTY 2 – Resistor 1/4W 10K Ohm, R1 & R2 (Source: MCM Electronics)
  • QTY 1 – Resistor 1/4W 220 Ohm, R3 (Source: MCM Electronics)
  • QTY 1 – DHT11 Digital Temperature & Humidity Sensor Moudle, SE1 (Source: Ebay)
  • QTY 1 – ATMEL ATtiny85, U1 (Source: Newark)

Materials List for Raspberry Pi Hat

  • Qty 1 – Perfboard (Source: Adafruit)
  • Qty 1 – GPIO Header for Raspberry Pi NOTE: The one you will need depends on which model of Raspberry Pi you are using. (Source: Adafruit 2×13 Header for original Raspberry Pi or 2×20 Header for newer Raspberry Pi)
  • QTY 1 – 3.5mm 4 Pole Audio Jack (Source: MCM Electronics NOTE: I ordered 27-9485 but received ones which look like 27-9487)
  • Optional items

Source Control

I have added the source files for the Hardware and Software onto GitHub. I did this so the community may have access to the files and any updates to them. I mainly did it because I was having a hard time remembering which set of files I last worked with especially if a few days went by when I could not work on the project. I think this is a win-win for me and anyone interested in this project.

The files are located at https://github.com/TeelSys/TeelSys_THL. When you first go to the page, it may look like there are mo project files included in the project. If that is the case it is because I am still trying to get everything working properly before I commit code to the master branch. You will see a button with the text “Branch: master” and a downward arrow. Click that button and select another branch such as “dev”. You will then see the project files in their current state.

If you wish to contribute, add a comment here or if you can request through GitHub, do that. I will reply once I see the request but keep in mind that it may be a few days.

Categories
Arduino Raspberry Pi

I2C Communications between Raspberry Pi and Arduino – Part Four

It has been two weeks since my last post but it has been out of frustration on porting the code over to the ATtiny85. The first thing that I ran into was that the Wire library is not supported on the ATtiny85. I needed to modify my code to work with the TinyWireS library. This did not seem too bad and worked once in a while. It was a bit frustrating as I followed examples and it appeared that I was doing everything correctly but that is typically how it goes when coding.

I finally took a look at the specs for the ATtiny85 and realized that memory may be my issue so I started to pare down the memory requirements. The Arduino IDE was not complaining but I recalled an posting that was published on Adafruit a couple of years ago called Arduino Memories. After rereading the article and looking at a couple of other references, I determined that I needed to tackle the memory is see if it was an issue.

At some point in my debugging, I had noticed that the examples for TinyWireS were utilizing a buffer and pointer method to do fast reads and writes. I had a significant switch statement on the request data handler so I removed that and went with the buffer option. By doing so I reserved a whopping 256 bytes for the buffer. This was a very stupid move which I realized when I took a look at the specs for the the ATtiny85. The ATtiny85 has only 512 bytes of RAM so I was consuming half of it for the buffer which did not leave much room for anything else.

I dropped the buffer size down to 32 bytes which helped a great deal. After reducing the size of the buffer, I could get communications between the ATtiny85 and the Raspberry Pi to work a few times before the communications stopped working. I further refined the code to reduce memory usage and swapped out the Adafruit DHT library for one written by Rob Tillaart for the DHT11 only.

Book1

With these modifications, I was able to get the code down to using 113 bytes of RAM and 4,918 bytes (60%) of Flash.

With these changes, the code works quite well but sometimes it appears that the ATtiny85 does not read the correct request from the Raspberry Pi. After some searching it was found that there is a known issue with the Raspberry Pi and clock stretching. It appears that there is a bug which has not been fixed yet if the slave stretches the clock at the right moment and the stretching is too short. The ATtiny85 implements I2C in software so this is going to happen at some point.One of the best articles on this issue is the Raspberry Pi I2C clock-stretching bug.

There are some suggested fixes which I need to read more to understand well enough to use. The most promising fix appears to use Python to perform I2C communication in software. The recommendation is to use the PiGPIO library.

Below is the code that I have thus far on the ATtiny85.

// Uses DHT from Rob instead of Adafruit
// http://playground.arduino.cc/Main/DHTLib
// http://playground.arduino.cc/Main/DHT11Lib


#include <TinyWireS.h>
#include <dht11.h>

#define SLAVE_ADDRESS 0x23

#define PIN_DHT 4
#define PIN_PHOTORESISTOR A3
#define PIN_LED 1

unsigned long previousMillis = 0;
#define interval 2500

#define bufferSz 32
byte dataBuffer[bufferSz] = { 32 };
uint8_t bufferIdx = 0;
boolean firstByteRead = false;

dht11 DHT11;

// Union used to convert float to byte array
union u_tag {
  byte b[4];
  float fval;
} fdata;

void setup() {
  pinMode(PIN_DHT, INPUT);
  pinMode(PIN_PHOTORESISTOR, INPUT);
  pinMode(PIN_LED, OUTPUT);

  digitalWrite(PIN_LED, HIGH);

  // initialize i2c as slave
  TinyWireS.begin(SLAVE_ADDRESS);

  // define callbacks for i2c communication
  TinyWireS.onReceive(receiveData);
  TinyWireS.onRequest(sendData);

  // Initialize dataBuffer
  for (int i = 0; i < bufferSz; i++) {
    dataBuffer[i] = 0xFF;
  }
  // Set LED to blink on each loop
  dataBuffer[3] = 2;
  // Load Model Info
  // T  S  0  0  0  0  0  1
  // 54 53 30 30 30 30 30 31
  String storeText = F("TS000001");
  bufferIdx = 0x10;
  for (int i = 0; i < storeText.length(); i++) {
    dataBuffer[bufferIdx] = storeText[i];
    bufferIdx++;
  }
  // Load Version Info
  // 0  0  0  0  0  0  0  3
  // 30 30 30 30 30 30 30 33
  storeText = F("00000003");
  bufferIdx = 0x18;
  for (int i = 0; i < storeText.length(); i++) {
    dataBuffer[bufferIdx] = storeText[i];
    bufferIdx++;
  }
}

void loop() {
  TinyWireS_stop_check();
  
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    if (dataBuffer[3] == 2) {
      digitalWrite(PIN_LED, !digitalRead(PIN_LED));
    }

    ReadDHT();
    ReadLightLevel();
  }
}

// callback for received data
void receiveData(uint8_t byteCount) {
  if (byteCount != 1)
  {
    // Sanity-check
    return;
  }

  while (TinyWireS.available()) {
    bufferIdx = TinyWireS.receive();
    firstByteRead = false;

    SetLedStatus();
  }
}

// callback for sending data
void sendData() {
  if (firstByteRead) {
    bufferIdx++;
  }

  firstByteRead = true;

  if(bufferIdx < 0 || bufferIdx >= bufferSz) {
    TinyWireS.send(0xFF);
    return;
  }

  TinyWireS.send(dataBuffer[bufferIdx]);
}

void ReadDHT() {
  int chk = DHT11.read(PIN_DHT);

  if(!chk==DHTLIB_OK) {
    return;
  }
  
  float humidity = (float)DHT11.humidity;
  float temperatureCelsius = (float)DHT11.temperature;
  
  SaveFloatToBuffer(0x04, temperatureCelsius);
  SaveFloatToBuffer(0x08, humidity);
}

void ReadLightLevel() {
  int photocellReading = analogRead(PIN_PHOTORESISTOR);
  float lightReading = ((float)photocellReading / 1023.0) * 100.0;
  SaveFloatToBuffer(0x0C, lightReading);
}

void SaveFloatToBuffer(uint8_t bufIdx, float val) { 
  dataBuffer[bufIdx] = 0;
  dataBuffer[bufIdx + 1] = 0;
  dataBuffer[bufIdx + 2] = 0;
  dataBuffer[bufIdx + 3] = 0;
  
  fdata.fval = val;

  dataBuffer[bufIdx] = fdata.b[3];
  dataBuffer[bufIdx + 1] = fdata.b[2];
  dataBuffer[bufIdx + 2] = fdata.b[1];
  dataBuffer[bufIdx + 3] = fdata.b[0];
  
  //dataBuffer[bufIdx] = (int)val;
}

void SetLedStatus() {
  if (bufferIdx > 2)
    return;

  dataBuffer[3] = 2;
  if (bufferIdx < 2) {
    digitalWrite(PIN_LED, bufferIdx);
    dataBuffer[3] = 0;
    if (digitalRead(PIN_LED) == HIGH) {
      dataBuffer[3] = 1;
    }
  }
}

Here is the code on the Raspberry Pi to verify that things are working.

#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>

#define CMD_GET_TEMPERATURE 1
#define CMD_GET_HUMIDITY 2
#define CMD_SET_LIGHT 3
#define CMD_SET_LED_ON 4
#define CMD_SET_LED_OFF 5
#define CMD_SET_LED_FLASH 6
#define CMD_GET_MODEL 250
#define CMD_GET_VERSION 251
#define CMD_GET_HELLO_WORLD 254

// The PiWeather board i2c address
#define ADDRESS 0x23

// The I2C bus: This is for V2 pi's. For V1 Model B you need i2c-0
char *devName = "/dev/i2c-0";
int file;
int devices[128];
int sensorDevices[128];

union u_tag {
	char b[4];
	float fval;
} fdata;

float computeHeatIndex(float temperature, float percentHumidity, int isFahrenheit);
float convertCtoF(float c);
float convertFtoC(float f);
void displayConnectedI2cDevices();
void dumpDeviceInfo(int deviceAddress);
void findAllI2cDevices();
void findI2cBus();
void findSensors();
float receiveFloat();
int receiveInt();
void receiveString(char *str, int bufSize);
int sendCommand(int deviceAddress, int cmdCode);

int main(int argc, char** argv) {
	// Look for the I2C bus device

  printf("I2C: Connecting\n");
	findI2cBus();
  
  // Find Devices
  findAllI2cDevices();
  
  // Display devices found (Simlar to i2cdetect -y 0)
  displayConnectedI2cDevices();
  
  dumpDeviceInfo(0x23);
  
  sendCommand(0x23, 0x04);
  float temperature = receiveFloat();
  sendCommand(0x23, 0x08);
  float percentHumidity = receiveFloat();
  sendCommand(0x23, 0x0C);
  float lightLevel = receiveFloat();
  
  printf("Temperature = %1.2f (C)\n", temperature);
  printf("Humidity = %1.2f\n", percentHumidity);
  printf("Light Level = %1.2f\n", lightLevel);

  close(file);
  return (EXIT_SUCCESS);
}

float computeHeatIndex(float temperature, float percentHumidity, int isFahrenheit) {
  // Using both Rothfusz and Steadman's equations
  // http://www.wpc.ncep.noaa.gov/html/heatindex_equation.shtml
  float hi;

  if (isFahrenheit==0)
    temperature = convertCtoF(temperature);

  hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094));

  if (hi > 79) {
    hi = -42.379 +
             2.04901523 * temperature +
            10.14333127 * percentHumidity +
            -0.22475541 * temperature*percentHumidity +
            -0.00683783 * pow(temperature, 2) +
            -0.05481717 * pow(percentHumidity, 2) +
             0.00122874 * pow(temperature, 2) * percentHumidity +
             0.00085282 * temperature*pow(percentHumidity, 2) +
            -0.00000199 * pow(temperature, 2) * pow(percentHumidity, 2);

    if((percentHumidity < 13) && (temperature >= 80.0) && (temperature <= 112.0))
      hi -= ((13.0 - percentHumidity) * 0.25) * sqrt((17.0 - abs(temperature - 95.0)) * 0.05882);

    else if((percentHumidity > 85.0) && (temperature >= 80.0) && (temperature <= 87.0))
      hi += ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2);
  }

  return isFahrenheit ? hi : convertFtoC(hi);
}

float convertCtoF(float c) {
  return c * (9.0/5.0) + 32;
}

float convertFtoC(float f) {
  return (f - 32) * (5.0/9.0);
}

void displayConnectedI2cDevices() {
	int idx=0;
	printf("     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f");
	for(idx=0; idx<=0x7F; idx++) {
		if(idx%16==0) {
			printf("\n%d0:",idx/16);
		}
		if(idx>0x07 && idx<0x78) {
			if(devices[idx]>0) {
				if(devices[idx]==-9) {
					printf(" UU");
				}
				else {
					printf(" %02x", idx);
				}
			}
			else {
				printf(" --");
			}
		}
		else {
				printf("   ");
		}
  }
  printf("\n");
}

void dumpDeviceInfo(int deviceAddress) {
	int i=0;
	
	sendCommand(deviceAddress, 0x03);
	
	for(i=0x03; i<0x20; i++) {
		int val = receiveInt();
		
		printf("0x%02x: 0x%02x (%d)\t%c\n", i, val, val, val);
	}
}

void findAllI2cDevices() {
	int idx=0;
  for(idx=0; idx<=0x7F; idx++) {
  	int device=0;
  	
  	if(idx>0x07 && idx<0x78) {
	  	if (ioctl(file, I2C_SLAVE, idx) < 0) {
	  		if(errno == EBUSY) {
	  			device = -9;
	  		}
	  		else {
		  		device = -1;
		  	}
	  	}
	  	else {
	  		char buf[1];
	  		if(read(file, buf, 1) == 1 && buf[0] >= 0) {
	  			device = idx;
	  		}
	  	}
  	}
  	
  	devices[idx] = device;
  }
}

void findI2cBus() {
	if ((file = open(devName, O_RDWR)) < 0) {
  	devName = "/dev/i2c-1";
  	if ((file = open(devName, O_RDWR)) < 0) {
	    fprintf(stderr, "I2C: Failed to access %d\n", devName);
	    exit(1);
	  }
  }
  
  printf("Found I2C bus at %s\n", devName);
}

void findSensors() {
	char *sensorType="TeelSys Data and Light Sensor";
	char buf[256];
	int idx=0;
	int sensorIdx=0;
	// sensorDevices
	// devices
	
	// Clear the sensorDevices array
	for(idx=0; idx<128; idx++) {
		sensorDevices[idx] = 0;
	}
	
  for(idx=0x08; idx<=0x78; idx++) {
  	int device=0;
  	
  	if(devices[idx]==idx) {
  		if(sendCommand(0x22, CMD_GET_MODEL)==1) {
  			int bufSize = sizeof(buf)/sizeof(buf[0]);
  			receiveString(buf, bufSize);
  			if(strlen(sensorType)==strlen(buf) && strcmp(sensorType, buf)==0) {
  				sensorDevices[sensorIdx]=devices[idx];
  				sensorIdx++;
  				printf("Found Sensor at: 0x%02x\n", devices[idx]);
  			}
  		}
  	}
  }
}

void receiveString(char *buf, int bufSize) {
  int charCount=0;
  
	if(read(file, buf, bufSize) == bufSize) {
		for(charCount=0; charCount<bufSize; charCount++) {
			int temp = (int) buf[charCount];
			
			if(temp==255) {
				buf[charCount]=0;
			}
		}
  }
}

int receiveChar() {
  char buf[1];
  char retVal = 0x00;
  
  if (read(file, buf, 1) == 1) {
  	retVal=buf[0];
  }
  
	usleep(10000);
  return retVal;
}

float receiveFloat() {	
	fdata.b[3] = 0;
	fdata.b[2] = 0;
	fdata.b[1] = 0;
	fdata.b[0] = 0;
	
	fdata.b[3] = receiveChar();
	fdata.b[2] = receiveChar();
	fdata.b[1] = receiveChar();
	fdata.b[0] = receiveChar();
	
	return fdata.fval;
	//return (float)fdata.b[3];
}

int receiveInt() {
  return (int)receiveChar();
}

int sendCommand(int deviceAddress, int cmdCode) {
	int retVal = 0;
	unsigned char cmd[16];
	cmd[0] = cmdCode;
	
	if (ioctl(file, I2C_SLAVE, deviceAddress) < 0) {
    fprintf(stderr, "I2C: Failed to acquire bus access/talk to slave 0x%x\n", deviceAddress);
    exit(1);
  }
  
  if (write(file, cmd, 1) == 1) {
  	// As we are not talking to direct hardware but a microcontroller we
    // need to wait a short while so that it can respond.
    //
    // 1ms seems to be enough but it depends on what workload it has
    usleep(10000);
    retVal = 1;
  }
  
  return retVal;
}

Running the Raspberry Pi program produces the following result.

pi@raspberrypi:~ $ ./testi2c07a
I2C: Connecting
Found I2C bus at /dev/i2c-0
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:                         -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- 23 -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
0x03: 0x02 (2)
0x04: 0x41 (65) A
0x05: 0xb8 (184)        ▒
0x06: 0x00 (0)
0x07: 0x00 (0)
0x08: 0x42 (66) B
0x09: 0x0c (12)

0x0a: 0x00 (0)
0x0b: 0x00 (0)
0x0c: 0x42 (66) B
0x0d: 0x2d (45) -
0x0e: 0xff (255)        ▒
0x0f: 0x80 (128)        ▒
0x10: 0x54 (84) T
0x11: 0x53 (83) S
0x12: 0x30 (48) 0
0x13: 0x30 (48) 0
0x14: 0x30 (48) 0
0x15: 0x30 (48) 0
0x16: 0x30 (48) 0
0x17: 0x31 (49) 1
0x18: 0x30 (48) 0
0x19: 0x30 (48) 0
0x1a: 0x30 (48) 0
0x1b: 0x30 (48) 0
0x1c: 0x30 (48) 0
0x1d: 0x30 (48) 0
0x1e: 0x30 (48) 0
0x1f: 0x33 (51) 3
Temperature = 23.00 (C)
Humidity = 35.00
Light Level = 43.50

 

Next step is to see if I can resolve the clock stretching issue and then connect to adafruit.io to post data. If it is not possible to address the clock stretching issue, it would be possible to identify when it occurs and reset the power to the I2C slave devices. I am trying to avoid that solution but I may need to resort to that solution.