ES32D26 sample code from Spain Aucavan
Share
// == Aucavan Domo ==
#include <WiFi.h>
#include <PubSubClient.h>
#include <Preferences.h>
// ======================= WiFi =======================
const char* ssid = "*";
const char* password = "*";
// ======================= MQTT =======================
const char* mqtt_server = "192.168.1.100";
const char* mqtt_user = "*";
const char* mqtt_pass = "";
WiFiClient espClient;
PubSubClient client(espClient);
// ======================= 74HC165 (Entradas digitales) =======================
#define PIN_LOAD 0
#define PIN_CLK 2
#define PIN_DATA 15
// ======================= 74HC595 (Relés) — ACTIVO EN BAJO =======================
const int CLOCK_595 = 22;
const int LATCH_595 = 23;
const int DATA_595 = 12;
const int OE_595 = 13; // <-- faltaba este punto y coma
// ======================= PWM =======================
#define VO1 25
#define VO2 26
// ======================= Entradas analógicas =======================
int analogPinsVI[] = {14, 33, 27, 32}; // 0–10 V
int analogPinsLI[] = {34, 39, 35, 36}; // 0–20 mA
// ======================= Estado =======================
// Entradas (NPN a GND -> activo=0)
uint8_t inputs = 0xFF;
// Relés activos en bajo: bit = 0 => ON, bit = 1 => OFF
// Valor inicial "seguro" con salidas deshabilitadas: todos OFF (1) hasta sincronizar
uint8_t outputs = 0xFF;
bool retained_seen = false; // vimos algún retenido MQTT al inicio (relés)
bool initial_outputs_applied = false;
// Calibración
float voltReal = 0;
float voltLeido = 0;
float calibrationFactor = 1.0;
// ======================= PWM setpoints (0..10) =======================
int vo1_sp = 0;
int vo2_sp = 0;
bool vo1_retained_seen = false;
bool vo2_retained_seen = false;
// Ventana de arranque para detectar retenidos
volatile bool start_window_active = false;
// ======================= Preferencias =======================
Preferences prefsCalib;
Preferences prefsRelays;
Preferences prefsPWM;
// ======================= Utilidades =======================
void setup_wifi() {
delay(10);
Serial.println("Conectando a WiFi...");
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi conectada: " + WiFi.localIP().toString());
}
// ----- 74HC595 -----
void Send_Bytes(uint8_t dat) { // MSB → LSB
for (uint8_t i = 8; i >= 1; i--) {
digitalWrite(DATA_595, (dat & 0x80) ? HIGH : LOW);
dat <<= 1;
digitalWrite(CLOCK_595, LOW);
digitalWrite(CLOCK_595, HIGH);
}
}
void Send_74HC595(uint8_t out) {
digitalWrite(LATCH_595, LOW);
Send_Bytes(out);
digitalWrite(LATCH_595, HIGH);
}
void setupShiftRegisterPins() {
pinMode(CLOCK_595, OUTPUT);
pinMode(LATCH_595, OUTPUT);
pinMode(DATA_595, OUTPUT);
pinMode(OE_595, OUTPUT);
// Mantener salidas deshabilitadas hasta tener estado inicial (anti-parpadeo)
digitalWrite(OE_595, HIGH);
}
void applyOutputsAndEnable() {
Send_74HC595(outputs);
// Habilitar salidas
digitalWrite(OE_595, LOW);
initial_outputs_applied = true;
}
void updateRelays(uint8_t relaysState) {
outputs = relaysState;
Send_74HC595(outputs);
}
void publishRelayStates() {
for (int i = 0; i < 8; i++) {
// activo-bajo: bit 0 => ON, bit 1 => OFF
int stateOn = (bitRead(outputs, i) == 0) ? 1 : 0;
client.publish(String("RELE/STATE/CH" + String(i + 1)).c_str(), String(stateOn).c_str());
}
}
// ----- 74HC165 -----
uint8_t readInputs165() {
uint8_t result = 0;
digitalWrite(PIN_LOAD, LOW);
digitalWrite(PIN_LOAD, HIGH);
for (uint8_t i = 0; i < 8; i++) {
result <<= 1;
digitalWrite(PIN_CLK, LOW);
if (digitalRead(PIN_DATA) == 0) { result |= 0x01; } // NPN a GND => activo = 0
digitalWrite(PIN_CLK, HIGH);
}
return result;
}
// ======================= PWM helpers =======================
void applyVO() {
int v1 = constrain(vo1_sp, 0, 10);
int v2 = constrain(vo2_sp, 0, 10);
ledcWrite(0, map(v1, 0, 10, 0, 255));
ledcWrite(1, map(v2, 0, 10, 0, 255));
}
// ======================= Helpers RELE/CHx =======================
void setRelayFromMsg(uint8_t idx, const String& msg) {
// Acepta "0"/"1" y también "ON"/"OFF"
int val;
if (msg.equalsIgnoreCase("ON")) val = 1;
else if (msg.equalsIgnoreCase("OFF")) val = 0;
else val = msg.toInt();
// Activo-bajo: 1 => 0, 0 => 1
bitWrite(outputs, idx, val ? 0 : 1);
}
// ======================= MQTT =======================
void callback(char* topic, byte* payload, unsigned int length) {
String t = String(topic);
String msg; msg.reserve(length);
for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
// Detectar mensajes durante la ventana de arranque (para relés)
static bool during_start_window = true;
// Control individual de cada relé (activo-bajo)
if (t == "RELE/CH1") { setRelayFromMsg(0, msg); retained_seen = retained_seen || during_start_window; }
else if (t == "RELE/CH2") { setRelayFromMsg(1, msg); retained_seen = retained_seen || during_start_window; }
else if (t == "RELE/CH3") { setRelayFromMsg(2, msg); retained_seen = retained_seen || during_start_window; }
else if (t == "RELE/CH4") { setRelayFromMsg(3, msg); retained_seen = retained_seen || during_start_window; }
else if (t == "RELE/CH5") { setRelayFromMsg(4, msg); retained_seen = retained_seen || during_start_window; }
else if (t == "RELE/CH6") { setRelayFromMsg(5, msg); retained_seen = retained_seen || during_start_window; }
else if (t == "RELE/CH7") { setRelayFromMsg(6, msg); retained_seen = retained_seen || during_start_window; }
else if (t == "RELE/CH8") { setRelayFromMsg(7, msg); retained_seen = retained_seen || during_start_window; }
// PWM (0..10) con persistencia y detección de retenidos
if (t == "OUT/VO1") {
int v = msg.toInt();
vo1_sp = constrain(v, 0, 10);
applyVO();
if (start_window_active) vo1_retained_seen = true;
prefsPWM.putInt("vo1_sp", vo1_sp);
}
if (t == "OUT/VO2") {
int v = msg.toInt();
vo2_sp = constrain(v, 0, 10);
applyVO();
if (start_window_active) vo2_retained_seen = true;
prefsPWM.putInt("vo2_sp", vo2_sp);
}
// Calibración
if (t == "CALIBRACION/VOLTR") {
voltReal = msg.toFloat();
prefsCalib.putFloat("voltReal", voltReal);
}
if (t == "CALIBRACION/VOLTL") {
voltLeido = msg.toFloat();
prefsCalib.putFloat("voltLeido", voltLeido);
}
if (voltLeido != 0) calibrationFactor = voltReal / voltLeido;
// Aplicar cambios a relés si corresponde
if (t.startsWith("RELE/CH")) {
updateRelays(outputs);
publishRelayStates();
prefsRelays.putUChar("haveState", 1);
prefsRelays.putUInt("relaysState", outputs);
}
}
void subscribeTopics() {
for (int i = 1; i <= 8; i++) client.subscribe(("RELE/CH" + String(i)).c_str()); // recibir retenidos
client.subscribe("OUT/VO1");
client.subscribe("OUT/VO2");
client.subscribe("CALIBRACION/VOLTR");
client.subscribe("CALIBRACION/VOLTL");
}
bool mqttConnect() {
Serial.print("Conectando a MQTT...");
bool ok = client.connect("ES32D26_Client", mqtt_user, mqtt_pass);
Serial.println(ok ? "Conectado" : "Fallo");
return ok;
}
void waitForRetainedWindow(unsigned long ms_window) {
// Durante esta ventana procesamos loop() para que lleguen los retenidos
start_window_active = true;
unsigned long start = millis();
while (millis() - start < ms_window) {
client.loop();
delay(5);
}
start_window_active = false;
}
void reconnect() {
while (!client.connected()) {
if (mqttConnect()) {
subscribeTopics();
// ventana para recibir retenidos al arranque
waitForRetainedWindow(800); // ajusta si tu broker es lento
// ---------- RELÉS: si no hubo retenidos, restaurar de NVS ----------
if (!retained_seen) {
uint8_t have = prefsRelays.getUChar("haveState", 0);
if (have) {
outputs = (uint8_t)prefsRelays.getUInt("relaysState", 0xFF);
Serial.println("Relés: restaurado estado de NVS");
} else {
outputs = 0xFF;
Serial.println("Relés: sin retenidos ni NVS, usando estado por defecto (OFF)");
}
} else {
Serial.println("Relés: estado inicial tomado de mensajes retenidos MQTT");
}
// ---------- PWM: decidir valores iniciales de VO1/VO2 ----------
if (!vo1_retained_seen) {
vo1_sp = prefsPWM.getInt("vo1_sp", vo1_sp); // usa último guardado
Serial.printf("VO1: sin retenido, recuperado de NVS: %d\n", vo1_sp);
} else {
Serial.printf("VO1: valor inicial desde MQTT retenido: %d\n", vo1_sp);
}
if (!vo2_retained_seen) {
vo2_sp = prefsPWM.getInt("vo2_sp", vo2_sp);
Serial.printf("VO2: sin retenido, recuperado de NVS: %d\n", vo2_sp);
} else {
Serial.printf("VO2: valor inicial desde MQTT retenido: %d\n", vo2_sp);
}
applyVO(); // asegura que quedan aplicados
// Aplica estado inicial de relés y habilita salidas una única vez
if (!initial_outputs_applied) {
applyOutputsAndEnable();
publishRelayStates();
}
} else {
Serial.print("Fallo MQTT, rc=");
Serial.println(client.state());
delay(2000);
}
}
}
// ======================= SETUP =======================
void setup() {
Serial.begin(115200);
// Preferencias
prefsCalib.begin("calib", false);
voltReal = prefsCalib.getFloat("voltReal", 0);
voltLeido = prefsCalib.getFloat("voltLeido", 0);
if (voltLeido != 0) calibrationFactor = voltReal / voltLeido;
prefsRelays.begin("relays", false);
prefsPWM.begin("pwm", false);
vo1_sp = prefsPWM.getInt("vo1_sp", 0); // lectura inicial (fallback)
vo2_sp = prefsPWM.getInt("vo2_sp", 0);
// Pines 74HC165
pinMode(PIN_LOAD, OUTPUT);
pinMode(PIN_CLK, OUTPUT);
pinMode(PIN_DATA, INPUT);
// Pines 74HC595 — mantener salidas deshabilitadas hasta sincronizar estado
setupShiftRegisterPins();
// PWM setup
ledcSetup(0, 5000, 8); ledcAttachPin(VO1, 0);
ledcSetup(1, 5000, 8); ledcAttachPin(VO2, 1);
applyVO(); // aplica el valor NVS inicial, se corregirá si llegan retenidos
// Conectar WiFi y MQTT
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
// Conexión inicial y sincronización de estado
reconnect();
}
// ======================= LOOP =======================
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
// Leer entradas digitales y publicar
inputs = readInputs165();
for (int i = 0; i < 8; i++) {
client.publish(("IN/IN" + String(i + 1)).c_str(), String(bitRead(inputs, i)).c_str());
}
// Entradas analógicas de voltaje (0–10 V)
for (int i = 0; i < 4; i++) {
int raw = analogRead(analogPinsVI[i]);
float voltage = (raw / 4095.0f) * 10.0f * calibrationFactor;
client.publish(("INV/VI" + String(i + 1)).c_str(), String(voltage, 2).c_str());
}
// Entradas de corriente (0–20 mA)
for (int i = 0; i < 4; i++) {
int raw = analogRead(analogPinsLI[i]);
float current = (raw / 4095.0f) * 20.0f;
client.publish(("INL/LI" + String(i + 1)).c_str(), String(current, 2).c_str());
}
delay(250);
}
1 comment
Hi. I bought the same board. I have already debugged the input/output. I made it in Arduino IDE in two files. Check your board.
***************************************************
//First file
//esp32_business.ino
#include “esp32_business.h”
extern IN_BYTE I;
extern QOUT_BYTE Q;
uint8_t SetOutRelay();
void ReadInputs ();
void setup ()
pinMode(shld_pin, OUTPUT);{
//set pins to output
pinMode(STcp,OUTPUT);
pinMode(SHcp,OUTPUT);
pinMode(DS,OUTPUT);
Serial.begin(115200);
// pinMode(ce_pin, OUTPUT);
pinMode(clk_pin, OUTPUT);
pinMode(data_pin, INPUT);
// выключаем регистр
digitalWrite(clk_pin, HIGH);
digitalWrite(shld_pin, HIGH);
}
/// Serial.printf(“ADC analog value = %d\n”, SetOut()); //Всегда вызывать в конце кода SetOutRelay(); //delay(1000);void loop()
{
//Всегда вызывать в кода
ReadInputs () ;
Q.CH1=I.IN1;
Q.CH2=I.IN2;
Q.CH3=I.IN3;
Q.CH4=I.IN4;
Q.CH5=I.IN5;
Q.CH6=I.IN6;
Q.CH7=I.IN7;
Q.CH8=I.IN8;
}
//Second Files
//esp32_business.ino
const int STcp = 23;//ST_CP
const int SHcp = 22;//SH_CP
const int DS = 12; //DS
const uint8_t data_pin = 15; //
const uint8_t shld_pin = 0; //
const uint8_t clk_pin = 2; // K
struct QOUT_BYTE
{
bool CH1;
bool CH2;
bool CH3;
bool CH4;
bool CH5;
bool CH6;
bool CH7;
bool CH8;
};
struct IN_BYTE
void ReadInputs () { digitalWrite(shld_pin, LOW); delayMicroseconds(5); digitalWrite(shld_pin, HIGH); delayMicroseconds(5); pinMode(clk_pin, OUTPUT); pinMode(data_pin, INPUT); uint8_t the_shifted = shiftIn(data_pin, clk_pin, MSBFIRST); if ((the_shifted & 0×01) == 0×01) I.IN1 = true; else I.IN1 = false; if ((the_shifted & 0×02) == 0×02) I.IN2 = true; else I.IN2 = false; if ((the_shifted & 0×04) == 0×04) I.IN3 = true; else I.IN3 = false; if ((the_shifted & 0×08) == 0×08) I.IN4 = true; else I.IN4 = false; if ((the_shifted & 0×10) == 0×10) I.IN5 = true; else I.IN5 = false; if ((the_shifted & 0×20) == 0×20) I.IN6 = true; else I.IN6 = false; if ((the_shifted & 0×40) == 0×40) I.IN7 = true; else I.IN7 = false; if ((the_shifted & 0×80) == 0×80) I.IN8 = true; else I.IN8 = false; // Serial.print(the_shifted); // Serial.print(" – ");{
bool IN1;
bool IN2;
bool IN3;
bool IN4;
bool IN5;
bool IN6;
bool IN7;
bool IN8;
};
IN_BYTE I;
QOUT_BYTE Q;
// Serial.println(the_shifted, BIN);
}
uint8_t SetOutRelay()
digitalWrite(STcp,LOW); //ground ST_CP and hold low for as long as you are transmitting shiftOut(DS,SHcp,MSBFIRST,outs); digitalWrite(STcp,HIGH); //pull the ST_CPST_CP to save the data{
uint8_t outs=0;
if (Q.CH1 == true ) outs |= 0×01;
if (Q.CH2 == true ) outs |= 0×02;
if (Q.CH3 == true ) outs |= 0×04;
if (Q.CH4 == true ) outs |= 0×08;
if (Q.CH5 == true ) outs |= 0×10;
if (Q.CH6 == true ) outs |= 0×20;
if (Q.CH7 == true ) outs |= 0×40;
if (Q.CH8 == true ) outs |= 0×80;
return outs;
}