diff --git a/tasmota/xdrv_15_pca9685.ino b/tasmota/xdrv_15_pca9685.ino index 48eae7f1f..f5249a03a 100644 --- a/tasmota/xdrv_15_pca9685.ino +++ b/tasmota/xdrv_15_pca9685.ino @@ -39,6 +39,7 @@ #define USE_PCA9685_FREQ 50 #endif +bool pca9685_inverted = false; // invert PWM for open-collector load bool pca9685_detected = false; uint16_t pca9685_freq = USE_PCA9685_FREQ; uint16_t pca9685_pin_pwm_value[16]; @@ -64,13 +65,22 @@ void PCA9685_Reset(void) { I2cWrite8(USE_PCA9685_ADDR, PCA9685_REG_MODE1, 0x80); PCA9685_SetPWMfreq(USE_PCA9685_FREQ); + pca9685_inverted = false; for (uint32_t pin=0;pin<16;pin++) { - PCA9685_SetPWM(pin,0,false); - pca9685_pin_pwm_value[pin] = 0; + PCA9685_SetPWM(pin,0,pca9685_inverted); + pca9685_pin_pwm_value[pin] = PCA9685_GetPWMvalue(0, pca9685_inverted); } Response_P(PSTR("{\"PCA9685\":{\"RESET\":\"OK\"}}")); } +uint16_t PCA9685_GetPWMvalue(uint16_t pwm, bool inverted) { + uint16_t pwm_val = pwm; + if (inverted) { + pwm_val = 4096-pwm; + } + return pwm_val; +} + void PCA9685_SetPWMfreq(double freq) { /* 7.3.5 from datasheet @@ -100,12 +110,13 @@ void PCA9685_SetPWM_Reg(uint8_t pin, uint16_t on, uint16_t off) { } void PCA9685_SetPWM(uint8_t pin, uint16_t pwm, bool inverted) { - if (4096 == pwm) { + uint16_t pwm_val = PCA9685_GetPWMvalue(pwm, inverted); + if (4096 == pwm_val) { PCA9685_SetPWM_Reg(pin, 4096, 0); // Special use additional bit causes channel to turn on completely without PWM } else { - PCA9685_SetPWM_Reg(pin, 0, pwm); + PCA9685_SetPWM_Reg(pin, 0, pwm_val); } - pca9685_pin_pwm_value[pin] = pwm; + pca9685_pin_pwm_value[pin] = pwm_val; } bool PCA9685_Command(void) @@ -130,6 +141,16 @@ bool PCA9685_Command(void) if (!strcmp(ArgV(argument, 1),"STATUS")) { PCA9685_OutputTelemetry(false); return serviced; } + if (!strcmp(ArgV(argument, 1),"INVERT")) { + if (paramcount > 1) { + pca9685_inverted = (1 == atoi(ArgV(argument, 2))); + Response_P(PSTR("{\"PCA9685\":{\"INVERT\":%i, \"Result\":\"OK\"}}"), pca9685_inverted?1:0); + return serviced; + } else { // No parameter was given for invert, so we return current setting + Response_P(PSTR("{\"PCA9685\":{\"INVERT\":%i}}"), pca9685_inverted?1:0); + return serviced; + } + } if (!strcmp(ArgV(argument, 1),"PWMF")) { if (paramcount > 1) { uint16_t new_freq = atoi(ArgV(argument, 2)); @@ -148,20 +169,20 @@ bool PCA9685_Command(void) uint8_t pin = atoi(ArgV(argument, 2)); if (paramcount > 2) { if (!strcmp(ArgV(argument, 3), "ON")) { - PCA9685_SetPWM(pin, 4096, false); + PCA9685_SetPWM(pin, 4096, pca9685_inverted); Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,4096); serviced = true; return serviced; } if (!strcmp(ArgV(argument, 3), "OFF")) { - PCA9685_SetPWM(pin, 0, false); + PCA9685_SetPWM(pin, 0, pca9685_inverted); Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,0); serviced = true; return serviced; } uint16_t pwm = atoi(ArgV(argument, 3)); if ((pin >= 0 && pin <= 15) && (pwm >= 0 && pwm <= 4096)) { - PCA9685_SetPWM(pin, pwm, false); + PCA9685_SetPWM(pin, pwm, pca9685_inverted); Response_P(PSTR("{\"PCA9685\":{\"PIN\":%i,\"PWM\":%i}}"),pin,pwm); serviced = true; return serviced; @@ -175,8 +196,10 @@ bool PCA9685_Command(void) void PCA9685_OutputTelemetry(bool telemetry) { ResponseTime_P(PSTR(",\"PCA9685\":{\"PWM_FREQ\":%i,"),pca9685_freq); + ResponseAppend_P(PSTR("\"INVERT\":%i,"), pca9685_inverted?1:0); for (uint32_t pin=0;pin<16;pin++) { - ResponseAppend_P(PSTR("\"PWM%i\":%i,"),pin,pca9685_pin_pwm_value[pin]); + uint16_t pwm_val = PCA9685_GetPWMvalue(pca9685_pin_pwm_value[pin], pca9685_inverted); // return logical (possibly inverted) pwm value + ResponseAppend_P(PSTR("\"PWM%i\":%i,"),pin,pwm_val); } ResponseAppend_P(PSTR("\"END\":1}}")); if (telemetry) {