// -------------------------------------------------------------------------------------------
// Basic Master Callback
// -------------------------------------------------------------------------------------------
//
// This creates a simple I2C Master device which when triggered will send/receive a text
// string to/from a Slave device.  It is intended to pair with a Slave device running the
// basic_slave sketch.  
//
// Functionally this sketch is similar to the basic_master sketch, except for three things:
// 1) It uses callbacks to handle the results and errors, instead of explicitly coding such
//    routines after each send/receive call.
// 2) It can optionally demonstrate background transfers by setting the NONBLOCKING flag (from
//    external observation these transfers will appear similar, but the LED duration will
//    be shorter as they exit immediately after initiating the transfer).
// 3) There is an added test to dump error diagnostics from the error counting system.
//
// Pull pin12 input low to send.
// Pull pin11 input low to receive.
// Pull pin10 input low to dump error diagnostics.
//
// This example code is in the public domain.
//
// -------------------------------------------------------------------------------------------

#include <i2c_t3.h>

// Memory
#define MEM_LEN 256
char databuf[MEM_LEN];
int count;

#define NONBLOCKING

void setup()
{
    pinMode(LED_BUILTIN,OUTPUT);    // LED
    digitalWrite(LED_BUILTIN,LOW);  // LED off
    pinMode(12,INPUT_PULLUP);       // Control for Send
    pinMode(11,INPUT_PULLUP);       // Control for Receive
    pinMode(10,INPUT_PULLUP);       // Control for Diagnostics

    // Setup for Master mode, pins 18/19, external pullups, 400kHz, 50ms default timeout
    Wire.begin(I2C_MASTER, 0x00, I2C_PINS_18_19, I2C_PULLUP_EXT, 400000);
    Wire.setDefaultTimeout(50000); // 50ms

    // Data init
    memset(databuf, 0, sizeof(databuf));
    count = 0;

    Serial.begin(115200);

    Wire.onTransmitDone(transmitDone);
    Wire.onReqFromDone(requestDone);
    Wire.onError(errorEvent);
}

void loop()
{
    uint8_t target = 0x66; // target Slave address

    // Send string to Slave
    //
    if(digitalRead(12) == LOW)
    {
        digitalWrite(LED_BUILTIN,HIGH);   // LED on

        // Construct data message
        sprintf(databuf, "Data Message #%d", count++);

        // Print message
        Serial.printf("Sending to Slave: '%s' ", databuf);

        // Transmit to Slave
        Wire.beginTransmission(target);   // Slave address
        Wire.write(databuf,strlen(databuf)+1); // Write string to I2C Tx buffer (incl. string null at end)
        #if defined(NONBLOCKING)
            Wire.sendTransmission();          // Transmit to Slave, non-blocking
        #else
            Wire.endTransmission();           // Transmit to Slave, blocking
        #endif

        // After send complete, callback will print result

        digitalWrite(LED_BUILTIN,LOW);    // LED off
        delay(100);                       // 100ms delay
    }

    // Read string from Slave
    //
    if(digitalRead(11) == LOW)
    {
        digitalWrite(LED_BUILTIN,HIGH);   // LED on

        // Print message
        Serial.print("Reading from Slave: ");

        // Read from Slave
        #if defined(NONBLOCKING)
            Wire.sendRequest(target, (size_t)MEM_LEN); // Read from Slave (string len unknown, request full buffer), non-blocking
        #else
            Wire.requestFrom(target, (size_t)MEM_LEN); // Read from Slave (string len unknown, request full buffer), blocking
        #endif

        // After request complete, callback will print result

        digitalWrite(LED_BUILTIN,LOW);    // LED off
        delay(100);                       // 100ms delay
    }

    // Diagnostics - print error count summary
    //
    if(digitalRead(10) == LOW)
    {
        digitalWrite(LED_BUILTIN,HIGH);   // LED on

        // Print errors
        Serial.print("\n");
        Serial.printf("I2C_ERRCNT_RESET_BUS: %d\n", Wire.getErrorCount(I2C_ERRCNT_RESET_BUS));
        Serial.printf("I2C_ERRCNT_TIMEOUT:   %d\n", Wire.getErrorCount(I2C_ERRCNT_TIMEOUT));
        Serial.printf("I2C_ERRCNT_ADDR_NAK:  %d\n", Wire.getErrorCount(I2C_ERRCNT_ADDR_NAK));
        Serial.printf("I2C_ERRCNT_DATA_NAK:  %d\n", Wire.getErrorCount(I2C_ERRCNT_DATA_NAK));
        Serial.printf("I2C_ERRCNT_ARBL:      %d\n", Wire.getErrorCount(I2C_ERRCNT_ARBL));
        Serial.printf("I2C_ERRCNT_NOT_ACQ:   %d\n", Wire.getErrorCount(I2C_ERRCNT_NOT_ACQ));
        Serial.printf("I2C_ERRCNT_DMA_ERR:   %d\n", Wire.getErrorCount(I2C_ERRCNT_DMA_ERR));

        // uncomment to zero all errors after dumping
        //for(uint8_t i=0; i < I2C_ERRCNT_DMA_ERR; i++) Wire.zeroErrorCount((i2c_err_count)i);

        digitalWrite(LED_BUILTIN,LOW);    // LED off
        delay(100);                       // 100ms delay
    }
}

//
// Trigger after Tx complete (outgoing I2C data)
//
void transmitDone(void)
{
    Serial.print("OK\n");
}

//
// Trigger after Rx complete (incoming I2C data)
//
void requestDone(void)
{
    Wire.read(databuf, Wire.available());
    Serial.printf("'%s' OK\n",databuf);
}

//
// Trigger on I2C Error
//
void errorEvent(void)
{
    Serial.print("FAIL - ");
    switch(Wire.status())
    {
    case I2C_TIMEOUT:  Serial.print("I2C timeout\n"); Wire.resetBus(); break;
    case I2C_ADDR_NAK: Serial.print("Slave addr not acknowledged\n"); break;
    case I2C_DATA_NAK: Serial.print("Slave data not acknowledged\n"); break;
    case I2C_ARB_LOST: Serial.print("Arbitration Lost, possible pullup problem\n"); Wire.resetBus(); break;
    case I2C_BUF_OVF:  Serial.print("I2C buffer overflow\n"); break;
    case I2C_NOT_ACQ:  Serial.print("Cannot acquire bus, possible stuck SDA/SCL\n"); Wire.resetBus(); break;
    case I2C_DMA_ERR:  Serial.print("DMA Error\n"); break;
    default:           break;
    }
}
