/* PCF8575  over I2C and Wire.h, with no other LIBS
CODE simple as possible 

SIMULATION : https://wokwi.com/projects/400225742336659457


MUST TEST ON HARDWARE

-------------NOTES-------------
INPUTS Connection: 
  Must be SET as HIGH, input action must place the port direct at GND.

OUTPUTS Connection: : can't drive more than 1mA, but can SINK +/- 25mA
    So, outputs just can´t work to power but SINKing current

    Vcc->LED/RELAY/etc/-> port PCF8575(sink to GND)
    can drice MOSFEtS ???? 


READING:
- Sense all port are connected via Resistor to Vcc, the reading values will be 1, 
when a input occours (place the port direct at GND ) the reading values will be 0, to invert
te reading: 1=Presses ( port=GND) and 0=notPressed ( port=Vdd) de "Wire.read" most be INVERTED with "~Wire.read"



- read the first 8 byte P00-P07 and then separated 8 byte P10-P17 maybe doesnt make to must sense,
the time and code that we spend, better read the 16 bits and then check what we need


make sense create a GLOBAl variable with 2 bytes to writte on PCF8575 ??
" volatile uint16_t gpio_writte = 0; ""




Inspered exempes:
  https://wokwi.com/projects/405889503258723329
  https://www.instructables.com/Using-The-PCF8575-i2c-io-Expander-To-Read-Inputs-/
  https://www.youtube.com/watch?v=mXMkgQf3fqU
  https://www.geeksforgeeks.org/store-two-numbers-in-one-byte-using-bit-manipulation/
*/

#include <Wire.h>
#define PCF8575_ADDRESS 0x20

#define input 1
#define Sink_on 0   // when it is LOW it will SINK
#define Sink_off 1   // when it is high it will SINK



// STORE all the values to SEND to PCF8575 
volatile uint16_t gpio_writte = 0;


void setup() { 
    Wire.begin();
    PCF8575_prepare_all_ports();
    Serial.begin(115200);
    delay(1000);
}

void loop() {
 
 read_exemple();
 writte_exemple();


  delay(1000);
    




}


void writte_exemple(){
    Serial.println("Sink_on  = LED ON");
    PCF8575_prepare_port_state(10,Sink_on);
    PCF8575_prepare_port_state(11,Sink_on);
    PCF8575_prepare_port_state(12,Sink_on);
    PCF8575_prepare_port_state(13,Sink_on);
    PCF8575_writte(PCF8575_ADDRESS, gpio_writte);
    delay(2000);
    Serial.println("Sink_oFF = LED OFF");
    PCF8575_prepare_port_state(10,Sink_off);
    PCF8575_prepare_port_state(11,Sink_off);
    PCF8575_prepare_port_state(12,Sink_off);
    PCF8575_prepare_port_state(13,Sink_off);
    PCF8575_writte(PCF8575_ADDRESS, gpio_writte);

    delay(500);
}


void read_exemple(){
      Serial.println("");    
    uint16_t teste = PCF8575_read_P00_P07(PCF8575_ADDRESS);
    //Serial.print("read_P00_P07: ");
    //Serial.println(teste);
    teste = PCF8575_read_P10_P17(PCF8575_ADDRESS);
    //Serial.print("read_P10_P17: ");
   // Serial.println(teste);

    Serial.println(PCF8575_read_indivual_from_all(teste,2));


    teste = PCF8575_read_all(PCF8575_ADDRESS);
    //Serial.print("ALL : ");
    //Serial.println(teste);

    Serial.print("bit : ");

    for (uint8_t i = 0; i < 16; i++) {

    Serial.print(PCF8575_read_indivual_from_all(teste,i));
    (i==7)? Serial.print("_") : Serial.print(",");


  }



}


void PCF8575_prepare_port_state(uint8_t port, bool state){
  (state==0)? gpio_writte &= ~  (1 << port) : gpio_writte |= (1 << port) ;
}

void PCF8575_prepare_all_ports(){
  // Set all PORTS to HIGH  = HIGH can read INPUTS - LED are in PULLUP
  // since we use  PCF8575 as a SINK/conenction the output to ground. all ports must be High
 
  for (uint8_t i = 0; i < 16; i++) {
    PCF8575_prepare_port_state(i,1);
    //delay(100);
  }

    PCF8575_writte(PCF8575_ADDRESS, gpio_writte);
}

void PCF8575_writte(uint8_t addr, uint16_t val ){
  Wire.beginTransmission(addr);
  Wire.write(val & 0xFF);         //  Low writing 8 bits
  Wire.write((val >> 8) & 0xFF);  // high writing 8 bits
  Wire.endTransmission();
}

uint8_t PCF8575_read_P00_P07(uint8_t addr ) {
  Wire.beginTransmission(addr);
  Wire.requestFrom(addr, 1);
  uint8_t Data_In = ~Wire.read();
  Wire.endTransmission();
  return Data_In;
}


uint8_t PCF8575_read_P10_P17(uint8_t addr ) {
    Wire.beginTransmission(addr);
    Wire.requestFrom(addr, 2);
    //1st read doenst matter
    uint8_t Data_In = Wire.read(); // Read 1 when not "botton" pressed: "~" invert bits
//Serial.print("read_P10_P17 1 : ");
//Serial.println(Data_In);
    Data_In = ~Wire.read(); // Read 1 when not "botton" pressed: "~" invert bits
//Serial.print("read_P10_P17 2 : ");
//Serial.println(Data_In);
    Wire.endTransmission();
    return Data_In;
}


uint16_t PCF8575_read_all(uint8_t addr ) {
  uint8_t Data_In_A =0;
  uint8_t Data_In_B =0;
  Wire.beginTransmission(addr);
  Wire.requestFrom(addr, 2);
  Data_In_A =~Wire.read();  // 
  //Serial.print("All 1 : ");
  //Serial.println(Data_In_A);
  Data_In_B = ~Wire.read(); // Read a uint16_t
  //Serial.print("All 2 : ");
  //Serial.println(Data_In_B);
  Wire.endTransmission();
  return (Data_In_B<<8) | (Data_In_A <<0);
}

// From a "value", read a "port_position" specific bit and return as 1(Pressed=port=gnd) or 0(NotPressed=port=Vdd)
bool PCF8575_read_indivual_from_all(uint16_t value, uint8_t port_position ) {
  return (value & (1 << port_position));
}
pcf8575Breakout
nINT
interrupt indicator
PCF8575 interrupt indicator