// Custom chips playground
// See https://link.wokwi.com/custom-chips-alpha for more info


// ======================= Copied Definitions ====================
// Copied some adafruit servo driver code rather than import the library - will do that later
// This explains the defines below etc...

#include <Wire.h>

// #define ENABLE_DEBUG_OUTPUT 

// REGISTER ADDRESSES
#define PCA9685_MODE1 0x00      /**< Mode Register 1 */
#define PCA9685_PRESCALE 0xFE     /**< Prescaler for PWM output frequency */

// MODE1 bits
#define MODE1_ALLCAL 0x01  /**< respond to LED All Call I2C-bus address */
#define MODE1_SUB3 0x02    /**< respond to I2C-bus subaddress 3 */
#define MODE1_SUB2 0x04    /**< respond to I2C-bus subaddress 2 */
#define MODE1_SUB1 0x08    /**< respond to I2C-bus subaddress 1 */
#define MODE1_SLEEP 0x10   /**< Low power mode. Oscillator off */
#define MODE1_AI 0x20      /**< Auto-Increment enabled */
#define MODE1_EXTCLK 0x40  /**< Use EXTCLK pin clock */
#define MODE1_RESTART 0x80 /**< Restart enabled */


#define FREQUENCY_OSCILLATOR 25000000 /**< Int. osc. frequency in datasheet */

#define PCA9685_PRESCALE_MIN 3   /**< minimum prescale value */
#define PCA9685_PRESCALE_MAX 255 /**< maximum prescale value */

// ======================= End of Copied Definitions ====================


// chip address. must match the wiring in diagram.json
const uint8_t _i2caddr = 0x5F;


static void begin(uint8_t prescale=0);
static void reset();
static void restart();
static void setPWMFreq(float f);

void setup() {
  Serial.begin(115200);
  Wire.begin();

  // initialise the PCA9685 chip
  begin();

  setPWM(_i2caddr, 0, 1500, 2000);

  delay(5000);

  reset();

  delay(1000);


  setPWM(_i2caddr, 0, 1000, 3000);
  delay(5000);
  
  restart();
  // setPWM(_i2caddr, 1, 4000, 3000);


}

void loop() {
  // static uint16_t ndx;

  // for (uint16_t i = 0; i < 8; i++) {
  //   // Serial.print(ndx);Serial.print(" spwm ");Serial.print(i); Serial.print(" on:"); Serial.print(i * 256); Serial.print(" off:");Serial.println((i+ 1 + (ndx %10))*256);
  //   setPWM(_i2caddr, i, i * 256, (i+ 1 + (ndx & 7))*256);
  // }
  // ndx++;
  delay(500);
}

void begin(uint8_t prescale=0) {
  restart();
  // set a default frequency
  setPWMFreq(1);
}

/*!
 *  @brief  Sends a reset command to the PCA9685 chip over I2C
 */
void restart() {
  write8(PCA9685_MODE1, MODE1_RESTART);
  delay(10);
}


void setPWMFreq(float freq) {

  // Range output modulation frequency is dependant on oscillator
  if (freq < 1)
    freq = 1;
  if (freq > 3500)
    freq = 3500; // Datasheet limit is 3052=50MHz/(4*4096)

  float prescaleval = ((FREQUENCY_OSCILLATOR / (freq * 4096.0)) + 0.5) - 1;
  if (prescaleval < PCA9685_PRESCALE_MIN)
    prescaleval = PCA9685_PRESCALE_MIN;
  if (prescaleval > PCA9685_PRESCALE_MAX)
    prescaleval = PCA9685_PRESCALE_MAX;
  uint8_t prescale = (uint8_t)prescaleval;

// #ifdef ENABLE_DEBUG_OUTPUT
  Serial.print("Final pre-scale: ");
  Serial.println(prescale);
// #endif

  uint8_t oldmode = read8(PCA9685_MODE1);
  uint8_t newmode = (oldmode & ~MODE1_RESTART) | MODE1_SLEEP; // sleep
  write8(PCA9685_MODE1, newmode);                             // go to sleep
  write8(PCA9685_PRESCALE, prescale); // set the prescaler
  write8(PCA9685_MODE1, oldmode);
  delay(5);
  // This sets the MODE1 register to turn on auto increment.
  write8(PCA9685_MODE1, oldmode | MODE1_RESTART | MODE1_AI);

#ifdef ENABLE_DEBUG_OUTPUT
  Serial.print("Mode now 0x");
  Serial.println(read8(PCA9685_MODE1), HEX);
#endif
}


void setPWM(int addr, int chan,  uint16_t on, uint16_t off ) {
#ifdef ENABLE_DEBUG_OUTPUT
  Serial.print("Setting PWM ");
  Serial.print(chan);
  Serial.print(": ");
  Serial.print(on);
  Serial.print("->");
  Serial.println(off);
#endif

  Wire.beginTransmission(addr);
  Wire.write(6 + 4 * chan);
  Wire.write(on);
  Wire.write(on >> 8);
  Wire.write(off);
  Wire.write(off >> 8);
  Wire.endTransmission();

}


uint8_t read8(uint8_t addr) {
  Wire.beginTransmission(_i2caddr);
  Wire.write(addr);
  Wire.endTransmission();

  Wire.requestFrom((uint8_t)_i2caddr, (uint8_t)1);
  return Wire.read();
}

void write8(uint8_t addr, uint8_t d) {
  Wire.beginTransmission(_i2caddr);
  Wire.write(addr);
  Wire.write(d);
  Wire.endTransmission();
}

void reset() {
  Wire.beginTransmission(0);
  Wire.write(6);
  Wire.endTransmission();
}

mega:SCL
mega:SDA
mega:AREF
mega:GND.1
mega:13
mega:12
mega:11
mega:10
mega:9
mega:8
mega:7
mega:6
mega:5
mega:4
mega:3
mega:2
mega:1
mega:0
mega:14
mega:15
mega:16
mega:17
mega:18
mega:19
mega:20
mega:21
mega:5V.1
mega:5V.2
mega:22
mega:23
mega:24
mega:25
mega:26
mega:27
mega:28
mega:29
mega:30
mega:31
mega:32
mega:33
mega:34
mega:35
mega:36
mega:37
mega:38
mega:39
mega:40
mega:41
mega:42
mega:43
mega:44
mega:45
mega:46
mega:47
mega:48
mega:49
mega:50
mega:51
mega:52
mega:53
mega:GND.4
mega:GND.5
mega:IOREF
mega:RESET
mega:3.3V
mega:5V
mega:GND.2
mega:GND.3
mega:VIN
mega:A0
mega:A1
mega:A2
mega:A3
mega:A4
mega:A5
mega:A6
mega:A7
mega:A8
mega:A9
mega:A10
mega:A11
mega:A12
mega:A13
mega:A14
mega:A15
pca9685Breakout
chip1:A0
chip1:A1
chip1:A2
chip1:A3
chip1:A4
chip1:LED0
chip1:LED1
chip1:LED2
chip1:LED3
chip1:LED4
chip1:LED5
chip1:LED6
chip1:LED7
chip1:VSS
chip1:LED8
chip1:LED9
chip1:LED10
chip1:LED11
chip1:LED12
chip1:LED13
chip1:LED14
chip1:LED15
chip1:OE
chip1:A5
chip1:EXTCLK
chip1:SCL
chip1:SDA
chip1:VDD
led0:A
led0:C
led1:A
led1:C
led2:A
led2:C
led3:A
led3:C
led4:A
led4:C
led5:A
led5:C
led6:A
led6:C
led7:A
led7:C
r1:1
r1:2
D0D1D2D3D4D5D6D7GNDLOGIC
logic1:D0
logic1:D1
logic1:D2
logic1:D3
logic1:D4
logic1:D5
logic1:D6
logic1:D7
logic1:GND