/*
 * Example use of C for getting temperature from BMP 180
 *   J Dean Brock <brock@unca.edu>
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <time.h>

/* Use bus 1 by default */
#if defined(I2CBUSNUM) && I2CBUSNUM==0
#define I2CDEVFILE "/dev/i2c-0"
#else
#define I2CDEVFILE "/dev/i2c-1"
#endif

#define BMP180ADDR               0x77
#define BMP180_CHIPID_REG        0xD0
#define BMP180_CHIPID_VALUE      0x55

/*
 * Following defines obtained AND selectively copied and modified from
 *   https://github.com/mkasick/android_kernel_samsung_d2spr
 * which was published with GNU GPL version 2 license
 */

/* Register definitions */
#define BMP180_TAKE_MEAS_REG            0xf4
#define BMP180_READ_MEAS_REG            0xf6
#define BMP180_MEAS_TEMP                0x2e

/*
 * EEPROM registers each is a two byte value so there is
 * an upper byte and a lower byte
 */
#define BMP180_EEPROM_AC1       0xaa
#define BMP180_EEPROM_AC2       0xac
#define BMP180_EEPROM_AC3       0xae
#define BMP180_EEPROM_AC4       0xb0
#define BMP180_EEPROM_AC5       0xb2
#define BMP180_EEPROM_AC6       0xb4
#define BMP180_EEPROM_B1        0xb6
#define BMP180_EEPROM_B2        0xb8
#define BMP180_EEPROM_MB        0xba
#define BMP180_EEPROM_MC        0xbc
#define BMP180_EEPROM_MD        0xbe
/*
 * End of stuff copied under GNU GPL 2
 */


/* Number of registers in EEPROM */
#define BMP180_EEPROM_NUMREGS ((BMP180_EEPROM_MD-BMP180_EEPROM_AC1+2)/2)

/*
 * i2c_smbus_read_word_data assumes little-endian
 * but it is big-endian
 */
int readBEWord(int fd, int addr) {
  int bew, bms, bls ;
  bew = i2c_smbus_read_word_data(fd, addr) ;
  if (bew<0) {
    char buff[64] ;
    sprintf(buff, "Failure reading addr %02X", addr) ;
    perror(buff) ;
    exit(1) ;
  }
  bms = bew & 0xFF ;
  if (bms >= 128)
    bms = bms - 256 ;
  bls = (bew >> 8) & 0xFF ;
  return bms*256 + bls ;
}

/*
 * Read the EEPROM into an array of integers.
 * This should only use one I2C bus transaction.
 */
int readEEPROM(int fd, int eeprom[BMP180_EEPROM_NUMREGS]) {
  char buff[2*BMP180_EEPROM_NUMREGS] ;
  int bytesRead ;
  int i, j ;
  bytesRead = i2c_smbus_read_i2c_block_data(fd, BMP180_EEPROM_AC1,
					    2*BMP180_EEPROM_NUMREGS, buff) ;
  for (i=0, j=0 ; i<bytesRead; i+=2, ++j) {
    int bms, bls ;
    bms = (signed char)buff[i] ;
    bls = buff[i+1] ;
    eeprom[j] = bms*256 + bls ;
  }
  return j ;
}

int main(int argc, char *argv[]) {
  int i2cfd ;
  int dataFromDevice ;
  int rc ;

  int eePROM[BMP180_EEPROM_NUMREGS] ;
  /*
   * These are the values mentioned on p 15.
   * Only a few of them are used to compute the temperature.
   */
  /* EEPROM registers */
  int ac1, ac2, ac3, ac4, ac5, ac6, b1, b2, mb, mc, md ;
  /* Needed in the computation */
  int b3, b4, b5, b6, b7, x1, x2 ;

  /* The results */
  int ut, t ;

  double c ;

  struct timespec snooze = { 0, 5000000 } ;   /* 5 msec or 5000000 nsec */

  /* open the I2C device */
  i2cfd = open(I2CDEVFILE, O_RDWR) ;
  if (i2cfd == -1) {
    perror("Open failed on " I2CDEVFILE ) ;
    exit(1) ;
  }
    
  /* Bind open file to BMP180 device */
  if (ioctl(i2cfd, I2C_SLAVE, BMP180ADDR) < 0) {
    perror("Failed to set device address") ;
    exit(1) ;
  }

  /* Test the device ID */
  dataFromDevice = i2c_smbus_read_byte_data(i2cfd, BMP180_CHIPID_REG) ;
  if (dataFromDevice<0) {
    perror("Failure reading device ID") ;
    exit(1) ;
  }
  if (dataFromDevice != BMP180_CHIPID_VALUE) {
    fprintf(stderr, "Unexpected device ID: %02X\n", (char)dataFromDevice) ;
    exit(1) ;
  }

  /* Read the EEPROM */
  if ((rc=readEEPROM(i2cfd, eePROM)) != BMP180_EEPROM_NUMREGS) {
    fprintf(stderr, "Not enough EEPROM: %d\n", rc) ;
    exit(1) ;
  }

  ac5 = eePROM[(BMP180_EEPROM_AC5 - BMP180_EEPROM_AC1)/2] ;
  ac6 = eePROM[(BMP180_EEPROM_AC6 - BMP180_EEPROM_AC1)/2] ;
  mc  = eePROM[(BMP180_EEPROM_MC  - BMP180_EEPROM_AC1)/2] ;
  md  = eePROM[(BMP180_EEPROM_MD  - BMP180_EEPROM_AC1)/2] ;

  /* Ask for a measurement */
  if (i2c_smbus_write_byte_data(i2cfd,
				BMP180_TAKE_MEAS_REG, BMP180_MEAS_TEMP)< 0) {
    perror("Failure writing measurement data") ;
    exit(1) ;
  }

  nanosleep(&snooze, (struct timespec *) NULL) ;           /* ZZZZZZZZZZ */

  ut = readBEWord(i2cfd, BMP180_READ_MEAS_REG) ;
  x1 = (ut-ac6)*ac5/32768 ;
  x2 = mc * 2048 / (x1 + md) ;
  b5 = x1 + x2 ;
  t = (b5 + 8) / 16 ;

#ifdef DEBUG
  printf("ut  = %6d\n", ut) ;
  printf("ac5 = %6d\n", ac5) ;
  printf("ac6 = %6d\n", ac6) ;
  printf("mc  = %6d\n", mc) ;
  printf("md  = %6d\n", md) ;
  printf("x1  = %6d\n", x1) ;
  printf("x2  = %6d\n", x2) ;
  printf("b5  = %6d\n", b5) ;
  printf("t   = %6d\n", t) ;
  printf("\n") ;
#endif

  c = t * 0.1 ;
  printf("{\"temperature\": %.1f}\n", c) ;
    
}
