/*
 * 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
#define BMP180_MEAS_PRESSURE            0x34

/*
 * 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)

/* oversamping */
#define OSS 1
const int oss = (OSS & 0x3) ;

/*
 * 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, short 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 ;
  unsigned char upBuff[3] ;

  short 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 */
  short ac1, ac2, ac3 ;
  unsigned short ac4, ac5, ac6 ;
  short b1, b2, mb, mc, md ;

  /* Needed in the computation */
  int b3 ;
  unsigned int b4 ;
  int b5, b6 ;
  unsigned int b7 ;
  int x1, x2, x3 ;

  /* The results */
  int ut, up, t, p ;

  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) ;
  }

  ac1 = eePROM[(BMP180_EEPROM_AC1 - BMP180_EEPROM_AC1)/2] ;
  ac2 = eePROM[(BMP180_EEPROM_AC2 - BMP180_EEPROM_AC1)/2] ;
  ac3 = eePROM[(BMP180_EEPROM_AC3 - BMP180_EEPROM_AC1)/2] ;
  ac4 = (unsigned short)eePROM[(BMP180_EEPROM_AC4 - BMP180_EEPROM_AC1)/2] ;
  ac5 = (unsigned short)eePROM[(BMP180_EEPROM_AC5 - BMP180_EEPROM_AC1)/2] ;
  ac6 = (unsigned short)eePROM[(BMP180_EEPROM_AC6 - BMP180_EEPROM_AC1)/2] ;
  mb  = eePROM[(BMP180_EEPROM_MB  - 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 ;
  */
  x1 = (ut - ac6) * ac5 >> 15 ;
  x2 = (mc << 11) / (x1 + md) ;
  b5 = x1 + x2 ;
  t = (b5 + 8) >> 4 ;

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

  snooze.tv_nsec = 7000000l * (oss+1) ;
  nanosleep(&snooze, (struct timespec *) NULL) ;           /* ZZZZZZZZZZ */

  if (i2c_smbus_read_i2c_block_data(i2cfd, BMP180_READ_MEAS_REG, 3, upBuff)
      != 3) {
    perror("Failure reading pressure data") ;
    exit(1) ;
  }

  up = (((unsigned int)upBuff[0]<<16) +
	((unsigned int)upBuff[1]<<8) +
	(unsigned int)upBuff[2]) >> (8-oss) ;

  b6 = b5 - 4000 ;
  x1 = (b2 * (b6 * b6 / 4096))/2048 ;
  x2 = ac2 * b6 / 2048 ;
  x3 = x1 + x2 ;
  b3 = (((ac1 * 4 + x3) << oss) + 2)/4 ;
  x1 = ac3 * b6 / 8192 ;
  x2 = (b1 * (b6 * b6 / 4096)) / 65536 ;
  x3 = ((x1 + x2) + 2)/ 4;
  b4 = ac4 * (unsigned int)(x3 + 32768)/ 32768 ;
  b7 = ((unsigned int)up - b3) * (50000 >> oss) ;
  if (b7 < 0x80000000) {
    p = (b7 * 2) / b4 ;
  } else {
    p = (b7 / b4) * 2 ;
  }
  x1 = (p / 256) * (p / 256) ;
  x1 = (x1 * 3038) / 65536 ;
  x2 = (-7357 * p) / 65536 ;
  p  = p + (x1 + x2 + 3791) / 16 ;

  /*
  b6 = b5 - 4000 ;
  x1 = (b2 * (b6 * b6 >> 12)) >> 11 ;
  x2 = ac2 * b6 >> 11 ;
  x3 = x1 + x2 ;
  b3 = ((((ac1 << 2) + x3) << oss) + 2) >> 2 ;
  x1 = ac3 * b6 >> 13 ;
  x2 = (b1 * (b6 * b6 >> 12)) >> 16 ;
  x3 = ((x1 + x2) + 2) >> 2 ;
  b4 = ac4 * (unsigned int)(x3 + 32768) >> 15 ;
  b7 = ((unsigned int)up - b3) * (50000 >> oss) ;
  if (b7 < 0x80000000) {
    p = (b7 + b7) / b4 ;
  } else {
    p = (b7 / b4) * 2 ;
  }
  x1 = (p >> 8) * (p >> 8) ;
  x1 = (x1 * 3038) >> 16 ;
  x2 = (-7357 * p) >> 16 ;
  p  = p + (x1 + x2 + 3791) / 16 ;
  */

#ifdef DEBUG
  printf("ut  = %10d\n", ut) ;
  printf("up  = %10d\n", up) ;
  printf("ac1 = %10d\n", ac1) ;
  printf("ac2 = %10d\n", ac2) ;
  printf("ac3 = %10d\n", ac3) ;
  printf("ac4 = %10u\n", ac4) ;
  printf("ac5 = %10u\n", ac5) ;
  printf("ac6 = %10u\n", ac6) ;
  printf("mc  = %10d\n", mc) ;
  printf("md  = %10d\n", md) ;
  printf("x1  = %10d\n", x1) ;
  printf("x2  = %10d\n", x2) ;
  printf("b1  = %10d\n", b1) ;
  printf("b2  = %10d\n", b2) ;
  printf("b3  = %10d\n", b3) ;
  printf("b4  = %10u\n", b4) ;
  printf("b5  = %10d\n", b5) ;
  printf("b6  = %10d\n", b6) ;
  printf("b7  = %10u\n", b7) ;
  printf("t   = %10d\n", t) ;
  printf("p   = %10d\n", p) ;
  printf("\n") ;
#endif

  fputs("Content-type: application/json\n\n", stdout) ;
  fprintf(stdout, "{{\"temperature\": %.1f},", t * 0.1) ;
  fprintf(stdout, " {\"pressure\": %d}}\n", p) ;
}

