// 16ch Temp&Hum
// by WANG Yanqi
// Created 4 Jan 2024

#include <Wire.h>

unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
const long interval = 1000; 
const uint8_t addr_hub[] = {0x70, 0x72};
const uint8_t addr_sen = 0x44;
const int size_hub = sizeof(addr_hub)/sizeof(addr_hub[0]);
uint8_t data[6];
uint8_t data_crc[2];
float temp;
float hum;

float fun_temp(float raw);
float fun_hum(float raw);
uint8_t crc8(const uint8_t* data, size_t length);

void setup() {

  Wire.begin();
  Serial.begin(9600);
  delay(5000);
  // Go through all the hubs
  for (int i = 0; i < size_hub; i++) {
    // Go through 8 ch. in a hub
    for (uint8_t ch = 0b00000001; ch != 0; ch = ch << 1) {
      // Open the ch.
      Wire.beginTransmission(addr_hub[i]);
      Wire.write(ch);
      Wire.endTransmission();
      // Soft reset sens
      Wire.beginTransmission(addr_sen);
      Wire.write(0x30);
      Wire.write(0xA2);
      Wire.endTransmission();
      delay(100);
    }
    // Close all the ch.
    Wire.beginTransmission(addr_hub[i]);
    Wire.write(0);
    Wire.endTransmission();
  }
}

void loop() {
  // Get the current number of milliseconds since the program started
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    // Go through all the hubs
    for (int i = 0; i < size_hub; i++) {
      // Go through 8 ch. in a hub
      for (uint8_t ch = 0b00000001; ch != 0; ch = ch << 1) {
        // Open the ch.
        Wire.beginTransmission(addr_hub[i]);
        Wire.write(ch);
        Wire.endTransmission();
        read_sen(addr_sen, data);
        // Check temp
        data_crc[0] = data[0];
        data_crc[1] = data[1];
        if (crc8(data_crc, sizeof(data_crc)) == data[2]) {
          temp = fun_temp((data[0] << 8) | data[1]);
          // Serial.print(temp);
        } else {
          temp = NAN;
          // temp = (data[0] << 8) | data[1];
          // Serial.print(temp, BIN);
        }
        // Serial.print(",");
        // Check hum
        data_crc[0] = data[3];
        data_crc[1] = data[4];
        if (crc8(data_crc, sizeof(data_crc)) == data[5]) {
          hum = fun_hum((data[3] << 8) | data[4]);
          // Serial.print(hum);
        } else {
          hum = NAN;
          // hum = (data[3] << 8) | data[4];
          // Serial.print(hum, BIN);
        }
        // Print data
        Serial.print(temp);
        Serial.print(",");
        Serial.print(hum);
        if (i != size_hub-1 || ch != 0b10000000) {
          Serial.print(";");
        }
      }
      // Close all the ch.
      Wire.beginTransmission(addr_hub[i]);
      Wire.write(0);
      Wire.endTransmission();
    }
    Serial.print("\n");
    previousMillis = currentMillis;
  }
}

void read_sen(uint8_t addr, uint8_t* data) {
  // Clock stretching, high repeatability
  Wire.beginTransmission(addr);
  Wire.write(0x2C);
  Wire.write(0x06);
  Wire.endTransmission();
  Wire.requestFrom(addr, 6);
  for (int i = 0; i < 6; i++) {
      data[i] = Wire.read();
  }
}

uint8_t crc8(const uint8_t* data, size_t length) {
  uint8_t crc = 0xFF; // Start with 0xFF for initialization
  for (size_t i = 0; i < length; ++i) {
    crc ^= data[i]; // XOR byte into least sig. byte of crc
    for (uint8_t j = 8; j; --j) { // Loop over each bit
      if (crc & 0x80) { // If the uppermost bit is 1...
        crc = (crc << 1) ^ 0x31; // ... shift left and XOR with the polynomial
      } else {
        crc <<= 1; // Otherwise, just shift left
      }
    }
  }
  // No final XOR
  return crc;
}

float fun_temp(float raw) {
  float temp = -45.0 + 175.0 * raw / 65535;
  return temp;
}

float fun_hum(float raw) {
  float hum = 100.0 * raw / 65535;
  return hum;
}