Update GPIOViewer

- Add ESP8266 PWM state logging
- Add ADC state functions
This commit is contained in:
Theo Arends 2024-01-07 15:10:19 +01:00
parent 2947fd8b06
commit 0d300aebff
12 changed files with 132 additions and 156 deletions

View File

@ -342,6 +342,14 @@ uint8_t ledcReadResolution(uint8_t chan) {
return res;
}
int32_t ledcReadDutyResolution(uint8_t pin) {
int32_t chan = analogGetChannel2(pin);
if (chan >= 0) {
return (1 << ledcReadResolution(chan));
}
return -1;
}
// Version of ledcRead that works for both Core2 and Core3
// Return -1 if pin is not configured as PWM
int32_t ledcRead2(uint8_t pin) {

View File

@ -98,6 +98,12 @@ void analogWrite(uint8_t pin, int val);
// Extended version that also allows to change phase
extern void analogWritePhase(uint8_t pin, uint32_t duty, uint32_t phase = 0);
//
// ledcReadDutyResolution - read the resolution
//
// return -1 if pin is not assigned to ledc
int32_t ledcReadDutyResolution(uint8_t pin);
//
// ledcRead2 - read the value of PWM
//

View File

@ -17,6 +17,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
int16_t analog_write_state[MAX_GPIO_PIN] = { -1 };
void AnalogWrite(uint8_t pin, int val) {
analog_write_state[pin] = val;
analogWrite(pin, val);
}
uint32_t AnalogRead(uint8_t pin) {
return analog_write_state[pin];
}
/***********************************************************************\
* PWM Control for ESP32
@ -223,7 +233,7 @@ void CmndPwm(void)
} else {
Settings->pwm_value_ext[pwm_index - MAX_PWMS_LEGACY] = XdrvMailbox.payload;
}
analogWrite(Pin(GPIO_PWM1, pwm_index), bitRead(TasmotaGlobal.pwm_inverted, pwm_index) ? Settings->pwm_range - XdrvMailbox.payload : XdrvMailbox.payload);
AnalogWrite(Pin(GPIO_PWM1, pwm_index), bitRead(TasmotaGlobal.pwm_inverted, pwm_index) ? Settings->pwm_range - XdrvMailbox.payload : XdrvMailbox.payload);
}
Response_P(PSTR("{"));
MqttShowPWMState(); // Render the PWM status to MQTT
@ -232,18 +242,21 @@ void CmndPwm(void)
}
void GpioInitPwm(void) {
for (uint32_t pin = 0; pin < MAX_GPIO_PIN; pin++) {
analog_write_state[pin] = -1; // No PWM pin (could be GPIO_PWM or GPIO_LED)
}
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
pinMode(Pin(GPIO_PWM1, i), OUTPUT);
if (i < TasmotaGlobal.light_type) {
// force PWM GPIOs to low or high mode if belongs to the light (always <5), see #7165
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
AnalogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
} else {
TasmotaGlobal.pwm_present = true;
if (i < MAX_PWMS_LEGACY) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
AnalogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
} else {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value_ext[i] : Settings->pwm_value_ext[i]);
AnalogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value_ext[i] : Settings->pwm_value_ext[i]);
}
}
}
@ -256,8 +269,8 @@ void ResetPwm(void)
{
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
// analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
AnalogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range : 0);
// AnalogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - Settings->pwm_value[i] : Settings->pwm_value[i]);
}
}
}

View File

@ -2191,7 +2191,7 @@ void LightSetOutputs(const uint16_t *cur_col_10) {
// AddLog(LOG_LEVEL_DEBUG_MORE, "analogWrite-%i 0x%03X", i, cur_col);
#else // ESP32
if (!Settings->flag4.zerocross_dimmer) {
analogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - cur_col : cur_col);
AnalogWrite(Pin(GPIO_PWM1, i), bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - cur_col : cur_col);
// AddLog(LOG_LEVEL_DEBUG_MORE, "analogWrite-%i 0x%03X", bitRead(TasmotaGlobal.pwm_inverted, i) ? Settings->pwm_range - cur_col : cur_col);
}
#endif // ESP32

View File

@ -6897,12 +6897,12 @@ void esp_pwm(int32_t value, uint32 freq, uint32_t channel) {
pwmpin[channel] = -value;
pinMode(pwmpin[channel], OUTPUT);
analogWriteFreq(freq);
analogWrite(pwmpin[channel], 0);
AnalogWrite(pwmpin[channel], 0);
} else {
if (value > 1023) {
value = 1023;
}
analogWrite(pwmpin[channel],value);
AnalogWrite(pwmpin[channel],value);
}
#endif // ESP32
}

View File

@ -15,22 +15,13 @@
#define XDRV_121 121
#define GV_PORT 8080
#define GV_PORT 5557
#define GV_SAMPLING_INTERVAL 100 // Relates to FUNC_EVERY_100_MSECOND
const char *GVRelease = "1.0.5";
const char *GVRelease = "1.0.7";
#define GV_BASE_URL "https://thelastoutpostworkshop.github.io/microcontroller_devkit/gpio_viewer/assets/"
#ifdef ESP32
// Global variables to capture PMW pins
const int GVMaxChannels = 64;
#endif // ESP32
#ifdef ESP8266
// Global variables to capture PMW pins
const int GVMaxChannels = MAX_PWMS;
#endif // ESP8266
const char HTTP_GV_PAGE[] PROGMEM =
"<!DOCTYPE HTML>"
"<html>"
@ -52,6 +43,7 @@ const char HTTP_GV_PAGE[] PROGMEM =
"</head>"
"<body>"
"<div class='grid-container'>"
"<div id='messageBox' class='message-box hidden'></div>"
"<header class='header'></header>"
// Image
"<div class='image-container'>"
@ -80,12 +72,10 @@ enum GVPinTypes {
struct {
WiFiClient WebClient;
ESP8266WebServer *WebServer;
int freeHeap;
uint32_t lastPinStates[MAX_GPIO_PIN];
int ledcChannelPin[GVMaxChannels][2];
int ledcChannelPinCount;
int ledcChannelResolution[GVMaxChannels][2];
int ledcChannelResolutionCount;
uint32_t resolution;
uint32_t freeHeap;
uint32_t freePSRAM;
bool sse_ready;
bool active;
} GV;
@ -102,117 +92,48 @@ String GVFormatBytes(size_t bytes) {
}
}
void GVPrintPWNTraps(void) {
#ifdef ESP32
for (uint32_t pin = 0; pin < GVMaxChannels; pin++) {
int32_t channel = analogGetChannel2(pin);
if (channel > -1) {
GV.ledcChannelPin[GV.ledcChannelPinCount][0] = pin;
GV.ledcChannelPin[GV.ledcChannelPinCount++][1] = channel;
uint8_t resolution = ledcReadResolution(channel);
GV.ledcChannelResolution[GV.ledcChannelResolutionCount][0] = channel;
GV.ledcChannelResolution[GV.ledcChannelResolutionCount++][1] = resolution;
}
}
#endif // ESP32
#ifdef ESP8266
uint32_t pwm_range = Settings->pwm_range + 1;
uint32_t resolution = 0;
while (pwm_range) {
resolution++;
pwm_range >>= 1;
}
for (uint32_t i = 0; i < MAX_PWMS; i++) { // Basic PWM control only
if (PinUsed(GPIO_PWM1, i)) {
int32_t channel = i;
GV.ledcChannelPin[GV.ledcChannelPinCount][0] = Pin(GPIO_PWM1, i);
GV.ledcChannelPin[GV.ledcChannelPinCount++][1] = channel;
GV.ledcChannelResolution[GV.ledcChannelResolutionCount][0] = channel;
GV.ledcChannelResolution[GV.ledcChannelResolutionCount++][1] = resolution;
}
}
#endif // ESP8266
AddLog(LOG_LEVEL_DEBUG, "IOV: %d pins are PWM", GV.ledcChannelPinCount);
for (int i = 0; i < GV.ledcChannelPinCount; i++) {
AddLog(LOG_LEVEL_DEBUG, "IOV: pin %d is using channel %d", GV.ledcChannelPin[i][0], GV.ledcChannelPin[i][1]);
}
AddLog(LOG_LEVEL_DEBUG, "IOV: %d channels are used", GV.ledcChannelResolutionCount);
for (int i = 0; i < GV.ledcChannelResolutionCount; i++) {
AddLog(LOG_LEVEL_DEBUG, "IOV: channel %d resolution is %d bits", GV.ledcChannelResolution[i][0], GV.ledcChannelResolution[i][1]);
}
}
int GVGetLedcChannelForPin(int pin) {
for (int i = 0; i < GV.ledcChannelPinCount; i++) {
if (GV.ledcChannelPin[i][0] == pin) {
return GV.ledcChannelPin[i][1];
}
}
return -1; // Pin not found, return -1 to indicate no channel is associated
}
int GVGetChannelResolution(int channel) {
for (int i = 0; i < GV.ledcChannelResolutionCount; i++) {
if (GV.ledcChannelResolution[i][0] == channel) {
return GV.ledcChannelResolution[i][1];
}
}
return -1; // Pin not found, return -1 to indicate no channel is associated
}
int GVMapLedcReadTo8Bit(int channel, uint32_t *originalValue) {
uint32_t maxDutyCycle = (1 << GVGetChannelResolution(channel)) - 1;
#ifdef ESP32
*originalValue = ledcRead(channel);
#endif // ESP32
#ifdef ESP8266
if (17 == channel) {
maxDutyCycle = (1 << 10) - 1; // 10 = ANALOG_RESOLUTION
*originalValue = AdcRead(channel, 2);
} else {
*originalValue = (channel < MAX_PWMS_LEGACY) ? Settings->pwm_value[channel] : Settings->pwm_value_ext[channel - MAX_PWMS_LEGACY];
}
#endif
return map(*originalValue, 0, maxDutyCycle, 0, 255);
}
int GVReadGPIO(int gpioNum, uint32_t *originalValue, uint32_t *pintype) {
int channel = GVGetLedcChannelForPin(gpioNum);
int value;
if (channel != -1) {
// This is a PWM Pin
value = GVMapLedcReadTo8Bit(channel, originalValue);
*pintype = PWMPin;
return value;
}
#ifdef ESP32
uint8_t analogChannel = analogGetChannel2(gpioNum);
if (analogChannel != 0 && analogChannel != 255) {
#endif // ESP32
#ifdef ESP8266
uint8_t analogChannel = gpioNum;
if (17 == analogChannel) {
#endif // ESP8266
// This is an analog pin
// Serial.printf("A Pin %d value=%d,channel=%d\n", gpioNum, value,analogChannel);
value = GVMapLedcReadTo8Bit(analogChannel, originalValue);
*pintype = analogPin;
return value;
}
else {
// This is a digital pin
uint32_t pin_type = GetPin(gpioNum) / 32;
/*
if (GPIO_NONE == pin_type) {
*pintype = digitalPin;
value = digitalRead(gpioNum);
*originalValue = 0;
return 0;
}
*/
#ifdef ESP32
int pwm_resolution = ledcReadDutyResolution(gpioNum);
if (pwm_resolution > 0) {
*pintype = PWMPin;
*originalValue = ledcRead2(gpioNum);
return changeUIntScale(*originalValue, 0, pwm_resolution, 0, 255); // bring back to 0..255
}
#endif // ESP32
#ifdef ESP8266
int pwm_value = AnalogRead(gpioNum);
if (pwm_value > -1) {
*pintype = PWMPin;
*originalValue = pwm_value;
int pwm_resolution = GV.resolution;
return changeUIntScale(*originalValue, 0, pwm_resolution, 0, 255); // bring back to 0..255
}
#endif // ESP8266
else if (AdcPin(gpioNum)) {
int adc_resolution = (1 << AdcResolution()) - 1;
*originalValue = AdcRead(gpioNum, 2);
*pintype = analogPin;
return changeUIntScale(*originalValue, 0, adc_resolution, 0, 255); // bring back to 0..255
}
*pintype = digitalPin;
int value = digitalRead(gpioNum);
*originalValue = value;
if (value == 1) {
return 256;
}
return 0;
}
}
void GVResetStatePins(void) {
@ -220,8 +141,8 @@ void GVResetStatePins(void) {
uint32_t pintype;
AddLog(LOG_LEVEL_INFO, "IOV: GPIOViewer Connected, sampling interval is " STR(GV_SAMPLING_INTERVAL) "ms");
for (int i = 0; i < MAX_GPIO_PIN; i++) {
GV.lastPinStates[i] = GVReadGPIO(i, &originalValue, &pintype);
for (uint32_t pin = 0; pin < MAX_GPIO_PIN; pin++) {
GV.lastPinStates[pin] = GVReadGPIO(pin, &originalValue, &pintype);
}
}
@ -236,21 +157,30 @@ void GVEventSend(const char *message, const char *event, uint32_t id) {
// Monitor GPIO Values
void GVMonitorTask(void) {
#ifdef ESP8266
// Can change on the fly
uint32_t pwm_range = Settings->pwm_range + 1;
GV.resolution = 0;
while (pwm_range) {
GV.resolution++;
pwm_range >>= 1;
}
#endif // ESP8266
uint32_t originalValue;
uint32_t pintype;
String jsonMessage = "{";
bool hasChanges = false;
for (uint32_t pin = 0; pin < MAX_GPIO_PIN; pin++) {
int currentState = GVReadGPIO(pin, &originalValue, &pintype);
for (int i = 0; i < MAX_GPIO_PIN; i++) {
int currentState = GVReadGPIO(i, &originalValue, &pintype);
if (originalValue != GV.lastPinStates[i]) {
if (originalValue != GV.lastPinStates[pin]) {
if (hasChanges) {
jsonMessage += ", ";
}
jsonMessage += "\"" + String(i) + "\": {\"s\": " + currentState + ", \"v\": " + originalValue + ", \"t\": " + pintype + "}";
GV.lastPinStates[i] = currentState;
jsonMessage += "\"" + String(pin) + "\": {\"s\": " + currentState + ", \"v\": " + originalValue + ", \"t\": " + pintype + "}";
GV.lastPinStates[pin] = currentState;
hasChanges = true;
}
}
@ -258,21 +188,27 @@ void GVMonitorTask(void) {
jsonMessage += "}";
if (hasChanges) {
// events->send(jsonMessage.c_str(), "gpio-state", millis());
GVEventSend(jsonMessage.c_str(), "gpio-state", millis());
}
uint32_t heap = ESP_getFreeHeap();
if (heap != GV.freeHeap) {
GV.freeHeap = heap;
// events->send(GVFormatBytes(GV.freeHeap).c_str(), "free_heap", millis());
GVEventSend(GVFormatBytes(GV.freeHeap).c_str(), "free_heap", millis());
}
#ifdef ESP32
if (UsePSRAM()) {
uint32_t psram = ESP.getFreePsram();
if (psram != GV.freePSRAM) {
GV.freePSRAM = psram;
GVEventSend(GVFormatBytes(GV.freePSRAM).c_str(), "free_psram", millis());
}
}
#endif // ESP32
}
void GVBegin(void) {
GVPrintPWNTraps();
GV.WebServer = new ESP8266WebServer(GV_PORT);
// Set CORS headers for global responses
GV.WebServer->sendHeader("Access-Control-Allow-Origin", "*");

View File

@ -1735,7 +1735,7 @@ void ZigbeeGlowPermitJoinLight(void) {
analogWritePhase(led_pin, led_power, 0);
}
#else
analogWrite(led_pin, TasmotaGlobal.ledlnk_inverted ? 1023 - led_power : led_power);
AnalogWrite(led_pin, TasmotaGlobal.ledlnk_inverted ? 1023 - led_power : led_power);
#endif
}
}

View File

@ -53,7 +53,7 @@ void BuzzerSet(uint32_t state) {
// Set 50% duty cycle for frequency output
// Set 0% (or 100% for inverted PWM) duty cycle which turns off frequency output either way
#ifdef ESP8266
analogWrite(Pin(GPIO_BUZZER), (state) ? Settings->pwm_range / 2 : 0); // set duty cycle for frequency output
AnalogWrite(Pin(GPIO_BUZZER), (state) ? Settings->pwm_range / 2 : 0); // set duty cycle for frequency output
#else
int32_t pin = Pin(GPIO_BUZZER);
if (analogAttach(pin, Buzzer.inverted) >= 0) {

View File

@ -218,7 +218,7 @@ void ShutterRtc50mS(void)
ShutterUpdateVelocity(i);
Shutter[i].real_position += Shutter[i].direction > 0 ? Shutter[i].pwm_velocity : (Shutter[i].direction < 0 ? -Shutter[i].pwm_velocity : 0);
Shutter[i].pwm_value = SHT_DIV_ROUND((Settings->shutter_pwmrange[1][i]-Settings->shutter_pwmrange[0][i]) * Shutter[i].real_position , Shutter[i].open_max)+Settings->shutter_pwmrange[0][i];
analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value);
AnalogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value);
break;
case SHT_COUNTER:
@ -572,7 +572,7 @@ void ShutterDecellerateForStop(uint8_t i)
while (RtcSettings.pulse_counter[i] < (uint32_t)(Shutter[i].target_position-Shutter[i].start_position)*Shutter[i].direction*ShutterGlobal.open_velocity_max/RESOLUTION/STEPS_PER_SECOND && missing_steps > 0) {
}
#ifdef ESP8266
analogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog
AnalogWrite(Pin(GPIO_PWM1, i), 0); // removed with 8.3 because of reset caused by watchog
#endif
#ifdef ESP32
TasmotaGlobal.pwm_value[i] = 0;
@ -629,12 +629,12 @@ void ShutterPowerOff(uint8_t i)
switch (ShutterGlobal.position_mode) {
case SHT_PWM_VALUE:
Shutter[i].pwm_value = SHT_DIV_ROUND((Settings->shutter_pwmrange[1][i]-Settings->shutter_pwmrange[0][i]) * Shutter[i].target_position , Shutter[i].open_max)+Settings->shutter_pwmrange[0][i];
analogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value);
AnalogWrite(Pin(GPIO_PWM1, i), Shutter[i].pwm_value);
AddLog(LOG_LEVEL_DEBUG, PSTR("SHT: PWM final %d"),Shutter[i].pwm_value);
char scmnd[20];
#ifdef SHUTTER_CLEAR_PWM_ONSTOP
// free the PWM servo lock on stop.
analogWrite(Pin(GPIO_PWM1, i), 0);
AnalogWrite(Pin(GPIO_PWM1, i), 0);
#endif
break;
}
@ -754,7 +754,7 @@ void ShutterStartInit(uint32_t i, int32_t direction, int32_t target_pos)
case SHT_COUNTER:
#ifdef ESP8266
analogWriteFreq(Shutter[i].pwm_velocity);
analogWrite(Pin(GPIO_PWM1, i), 0);
AnalogWrite(Pin(GPIO_PWM1, i), 0);
#endif
#ifdef ESP32
analogWriteFreq(PWM_MIN,Pin(GPIO_PWM1, i));

View File

@ -194,7 +194,7 @@ void PWMDimmerSetBrightnessLeds(int32_t bri)
SetLedPowerIdx(led, bri > level);
} else {
uint16_t pwm_led_bri = changeUIntScale((bri > level ? bri - level : 0), 0, step, 0, Settings->pwm_range);
analogWrite(Pin(GPIO_LED1, led), bitRead(TasmotaGlobal.led_inverted, led) ? Settings->pwm_range - pwm_led_bri : pwm_led_bri);
AnalogWrite(Pin(GPIO_LED1, led), bitRead(TasmotaGlobal.led_inverted, led) ? Settings->pwm_range - pwm_led_bri : pwm_led_bri);
}
}
}

View File

@ -124,7 +124,7 @@ bool Sm16716SetChannels(void)
for (uint32_t i = 3; i < Light.subtype; i++) {
if (PinUsed(GPIO_PWM1, i-3)) {
//AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION "Cur_Col%d 10 bits %d, Pwm%d %d"), i, cur_col[i], i+1, curcol);
analogWrite(Pin(GPIO_PWM1, i-3), bitRead(TasmotaGlobal.pwm_inverted, i-3) ? Settings->pwm_range - cur_col_10bits[i] : cur_col_10bits[i]);
AnalogWrite(Pin(GPIO_PWM1, i-3), bitRead(TasmotaGlobal.pwm_inverted, i-3) ? Settings->pwm_range - cur_col_10bits[i] : cur_col_10bits[i]);
}
}
*/

View File

@ -324,6 +324,19 @@ void AdcInit(void) {
}
}
uint32_t AdcResolution(void) {
return ANALOG_RESOLUTION;
}
bool AdcPin(uint32_t pin) {
for (uint32_t idx = 0; idx < Adcs.present; idx++) {
if (pin == Adc[idx].pin) {
return true;
}
}
return false;
}
uint16_t AdcRead(uint32_t pin, uint32_t factor) {
// factor 1 = 2 samples
// factor 2 = 4 samples