/* Mutrux Roller Lid Controller
EEPROM Map
Addr 0x00-0x01 : open/close counter;
Addr 0x02-0x03 : stop by open/close/blocked
Addr 0x04-0x05 : stop by button
Addr 0x06-0x07 : stop by stuck
Addr 0x08-0x09 : stop by ovetime
Addr 0x0A-0x0B : Dyty Cycle
Addr 0x0C-0x0D : Freq
ADdr 0x0E-0x0F : max_runtime
Addr 0x10-0x13 : start limited current
Addr 0x14-0x17 : stop limited current
Addr 0x18-0x19 : vOffset
.
Addr 0x20-0x2F : password
Hardware
- ESP32 V1
- BTS7960 43A H-Bridge PWM Drive
https://pdf1.alldatasheet.com/datasheet-pdf/view/152658/INFINEON/BTS7960B.html
- Relay Module 2 channel 10A 3.3-5V
- 20A Current Sensor Module (ACS712-20A) 20A OUTPUT 100mV/A
https://www.allnewstep.com/product/162/20a-current-sensor-module-acs712-20a
- MOSFET Electronic switch control pulse trigger optocoupler (for LED bar) Max 5 amp
https://www.allnewstep.com/product/3282/mosfet-electronic-switch-control-pulse-trigger-optocoupler-%E0%B9%82%E0%B8%A1%E0%B8%94%E0%B8%B9%E0%B8%A5%E0%B8%82%E0%B8%B1%E0%B8%9A%E0%B8%A1%E0%B8%AD%E0%B9%80%E0%B8%95%E0%B8%AD%E0%B8%A3%E0%B9%8C-%E0%B8%AB%E0%B8%A3%E0%B8%B5%E0%B9%88%E0%B9%84%E0%B8%9F-led-%E0%B9%81%E0%B8%9A%E0%B8%9A-pwm-5-36v-
Command List
"L" : LEFT
"R" : RIGHT
"LED" : Toggle LED bar
"RESTART" : restart board
"REPORT" : report usage status
"FACTORY" : factory reset (hold 3 sec)
"MELODY" : play song
"CONFIG" : set parameter
*/
#include <EEPROM.h>
#include <BluetoothSerial.h>
#include <esp_task_wdt.h>
#define WDT_TIMEOUT 3//watchdog timeout 3 second
//Pin out
#define IS_PIN 39 //BTS7960 current read
#define FACTORY_RESET_BTN 34//factory reset button R pullUP
#define LED_BTN 35//LED button need R pull UP
#define LEFT_BTN 32//Right button
#define RIGHT_BTN 33//Left button
#define L_PWM 25 //BTS7960 L PWM
#define R_PWM 26 //BTS7960 R PWM
#define LR_EN 27 //BTS7960
#define RELAY_EN 14 //relay module
#define LED_BAR 12//LED bar pin
#define BUZZER_PIN 13//buzzer pin
#define LED_BUILTIN 2 //onboard LED_BUILTIN
// setting PWM properties
#define ledChannelL 0
#define ledChannelR 1
#define buzzerChannel 2
#define resolution 10 //1024
// Variable
bool FLAG_LIVE = false;//blue led blink flag;
bool currentRead = true;
bool FLAG_LED = false;//led light flag
bool FLAG_BEEP = false;// FLAG_BEEPing flag
bool FLAG_L = false;
bool FLAG_R = false;
int64_t currentTime = 0;
int64_t lastTime = 0;
int64_t buzzerTime = 0;
int64_t ledTime = 0;
int64_t factoryTime = 0;
int PWM = 0;//0-1023 pulse width
int64_t watchdogTime = 0;
//read current
//L_IS<--R 1K -->Gnd read 1v = 1mA = 1Amp
double amp_read = 0;//current amp R
double steady_current = 0;//steady current after run for a while
float adjust = 1.0;//multiplier to correct current reading
String message = "";
//configuration parameter
uint16_t DutyCycle = 0;//speed control %
uint16_t freq = 0;//400 hz
int max_runtime = 0;//max lid open/close time is 5 sec
double limit_current = 0;//overload current
double stop_current_threshold = 0;//max volt to stop stuck.
int vOffset = 0;//2500 mv Voffset for ACS712-20A
String password = "";//max 16
byte averageCounter = 0;
long volt_read = 0; //mvolt
String parameter[10];//configuration array for bluetooth setup
//COMMAND=dutycycle,freq,max_runtime,limit_current,stop_current,voffset
//DEFAULT configuration parameter
uint16_t DEFAULT_DutyCycle = 30;//speed control %
uint16_t DEFAULT_freq = 200;//400 hz
int DEFAULT_max_runtime = 10000;//max lid open/close time is 10 sec
double DEFAULT_limit_current = 11.0;//amp overload current
uint16_t DEFAULT_stop_current_threshold = 30;//30% of running current
int DEFAULT_vOffset = 2500;//2500 mv Voffset for ACS712-20A
String DEFAULT_password = "12345678";//max 16
BluetoothSerial SerialBT;
/*-----------------------*/
static void resetEEPROM() {
//change eeprom to default and restart
EEPROM.writeUShort(0x00,0);//open/close counter;
EEPROM.writeUShort(0x02,0);//stop by open/close/blocked
EEPROM.writeUShort(0x04,0);//stop by button
EEPROM.writeUShort(0x06,0);//stop by stuck
EEPROM.writeUShort(0x08,0);//stop by ovetime
EEPROM.writeUShort(0x0A,DEFAULT_DutyCycle);//default pwm 80%
EEPROM.writeUShort(0x0C,DEFAULT_freq);//default frequency
EEPROM.writeUShort(0x0E,DEFAULT_max_runtime);//default max runtime 5 sec
EEPROM.writeFloat(0x10,DEFAULT_limit_current);//limit_current_max 4 bytes
EEPROM.writeFloat(0x14,DEFAULT_stop_current_threshold);//stop_current_threshold 4 bytes
EEPROM.writeUShort(0x18,DEFAULT_vOffset);//v Offset 2 bytes
EEPROM.writeString(0x20,DEFAULT_password);//default password 16 byte
EEPROM.commit();
}
/*-----------------------*/
static void report() {
uint16_t eepromread = EEPROM.readUShort(0x0A);//0x0A-0x0B
if (eepromread != 0) DutyCycle = eepromread;//if empty use default value
eepromread = EEPROM.readUShort(0xC);//0x0C-0x0D
if (eepromread != 0) freq = eepromread;
eepromread = EEPROM.readShort(0xE);//0x0E-0x0F
if (eepromread != 0) max_runtime = eepromread;
float eepromreadf = EEPROM.readFloat(0x10);//limit_current_max 4 bytes
if (eepromread != 0) limit_current = eepromreadf;
eepromreadf = EEPROM.readFloat(0x14);//stop_current_threshold 4 bytes
if (eepromread != 0) stop_current_threshold = eepromreadf;
eepromread = EEPROM.readUShort(0x18);//0x18-0x19
if (eepromread != 0) vOffset = eepromread;
Serial.printf("- CONFIGURATION -\nPWM: %d%%\nFreq: %d Hz\nMax Runtime: %d mSec\nOverload Limited Current: %f Amp\nStop Current Threshold: %f %%\nRead vOffset: %d mVolt\n",DutyCycle,freq,max_runtime,limit_current,stop_current_threshold,vOffset);
SerialBT.printf("- CONFIGURATION -\nPWM: %d%%\nFreq: %d Hz\nMax Runtime: %d mSec\nOverload Limited Current: %f Amp\nStop Current Threshold: %f %%\nRead vOffset: %d mVolt\n",DutyCycle,freq,max_runtime,limit_current,stop_current_threshold,vOffset);
Serial.printf("- USAGE REPORT -\nOpen/Close : %d times\n",EEPROM.readUShort(0x00));
Serial.printf("Normal Stop : %d times\n",EEPROM.readUShort(0x02));
Serial.printf("Button Stop : %d times\n",EEPROM.readUShort(0x04));
Serial.printf("Stuck Stop : %d times\n",EEPROM.readUShort(0x06));
Serial.printf("Overtime Stop: %d times\n",EEPROM.readUShort(0x08));
SerialBT.printf("- USAGE REPORT -\nOpen/Close : %d times\n",EEPROM.readUShort(0x00));
SerialBT.printf("Normal Stop : %d times\n",EEPROM.readUShort(0x02));
SerialBT.printf("Button Stop : %d times\n",EEPROM.readUShort(0x04));
SerialBT.printf("Stuck Stop : %d times\n",EEPROM.readUShort(0x06));
SerialBT.printf("Overtime Stop: %d times\n",EEPROM.readUShort(0x08));
}
/*-----------------------*/
static void motor_left() {
lastTime = millis();
FLAG_L = true;
digitalWrite(LED_BAR,HIGH);//turn on led bar
digitalWrite(RELAY_EN,LOW);//enable safety relay
digitalWrite(LR_EN,HIGH);//enable BTS7960
ledcWrite(ledChannelR,0);
ledcWrite(ledChannelL,PWM);
buzzerTime = lastTime;
Serial.print("LEFT -> ");
SerialBT.println("LEFT -> ");
uint16_t count = EEPROM.readUShort(0x00);
EEPROM.writeUShort(0x00,count+1);
}
static void motor_right() {
lastTime = millis();
FLAG_R = true;
digitalWrite(LED_BAR,HIGH);//turn on led bar
digitalWrite(RELAY_EN,LOW);//enable safety relay
digitalWrite(LR_EN,HIGH);//enable BTS7960
ledcWrite(ledChannelL,0);
ledcWrite(ledChannelR,PWM);
buzzerTime = lastTime;
Serial.print("RIGHT -> ");
SerialBT.print("RIGHT -> ");
uint16_t count = EEPROM.readUShort(0x00);
EEPROM.writeUShort(0x00,count+1);
}
static void motor_stop(String reason) {
ledcWriteTone(buzzerChannel,0);
FLAG_LED = false;
FLAG_BEEP = false;
FLAG_L = false;
FLAG_R = false;
//disable pwm drive and relay
digitalWrite(LED_BAR,LOW);//turn off led bar
digitalWrite(RELAY_EN,HIGH);
digitalWrite(LR_EN,LOW);
ledcWrite(ledChannelL,0);
ledcWrite(ledChannelR,0);
Serial.print(reason);
Serial.printf(" %f Amp\n",amp_read);
SerialBT.print(reason);
SerialBT.printf(" %f Amp\n",amp_read);
ledcWriteTone(buzzerChannel,0);
}
static void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param){
if(event == ESP_SPP_SRV_OPEN_EVT){
Serial.println("Client Connected");
SerialBT.println("Mutrux Roller Lid v1.0");
}
if(event == ESP_SPP_CLOSE_EVT ){
Serial.println("Client disconnected");
}
}
/*-----------------------*/
void setup() {
//Pin mode config
pinMode(LEFT_BTN, INPUT_PULLUP);//push button
pinMode(RIGHT_BTN, INPUT_PULLUP);//push button
pinMode(LED_BTN, INPUT_PULLUP);//push button
pinMode(FACTORY_RESET_BTN, INPUT_PULLUP);//pushbutton
pinMode(LED_BUILTIN, OUTPUT);//internal led
pinMode(RELAY_EN, OUTPUT);//enable+relay
pinMode(LR_EN, OUTPUT);//enable+relay
pinMode(LED_BAR, OUTPUT);//LED light
//init output
digitalWrite(RELAY_EN,HIGH);//turn off Relay
digitalWrite(LR_EN,LOW);//turn off BTS7960
digitalWrite(LED_BAR,LOW);//turn off light
digitalWrite(LED_BUILTIN,LOW);//turn off led
// initialize serial communication at 115200 bits per second:
Serial.begin(115200);
Serial.println("Mutrux Roller Lid v1.0");
//SerialBT.register_callback(callback);
/*
if(!SerialBT.begin("Mutrux")){
Serial.println("Bluetooth Error!");
}else{
Serial.println("Bluetooth OK");
}*/
//Load EEPROM configuration
EEPROM.begin(128);
String eepromread2 = EEPROM.readString(0x20);//0x20-0x2F
if (eepromread2 !="") password = eepromread2;
Serial.printf("Password: %s\n",password);
report();//report usage data
//init adc
// sensor.calibrate();
analogReadResolution(resolution);
if (!adcAttachPin(IS_PIN)) {
Serial.println("Error current reading sensor!");
}
//calibrate ACS712 volt offset without current
for (int i = 0; i < 100; i++) {
// volt_read += analogReadMilliVolts(IS_PIN);
volt_read += analogRead(IS_PIN)*3300/1024;
}
vOffset = volt_read/100;
EEPROM.writeUShort(0x18,vOffset);//v Offset 2 bytes
// configure LED_BUILTIN PWM functionalitites
ledcSetup(ledChannelL, freq, resolution);
ledcSetup(ledChannelR, freq, resolution);
ledcSetup(buzzerChannel, 1500, resolution);
// attach the channel to the GPIO to be controlled
ledcAttachPin(L_PWM, ledChannelL);
ledcAttachPin(R_PWM, ledChannelR);
ledcAttachPin(BUZZER_PIN, buzzerChannel);
ledcWrite(ledChannelL,0);
ledcWrite(ledChannelR,0);
PWM = round(DutyCycle*10.24);
//beep start
ledcWriteTone(buzzerChannel,1500);
delay(200);
ledcWriteTone(buzzerChannel,0);
delay(200);
ledcWriteTone(buzzerChannel,1500);
delay(200);
ledcWriteTone(buzzerChannel,0);
esp_task_wdt_init(WDT_TIMEOUT, true); //enable panic so ESP32 restarts
esp_task_wdt_add(NULL); //add current thread to WDT watch
Serial.println("Ready!");
SerialBT.println("Ready!");
}
/*-----------------------*/
void loop() {
// read the analog / millivolts value for pin 2:
currentTime = millis();
//100mV/amp
// amp_read = sensor.getCurrentDC();
// amp_read = analogRead(IS_PIN)*3300/1024-vOffset;
//watchdog reset every 2 sec, if more than 3 will be reboot
if (currentTime - watchdogTime > 2000) {
esp_task_wdt_reset();
FLAG_LIVE = !FLAG_LIVE;
digitalWrite(LED_BUILTIN,FLAG_LIVE);//turn off led
watchdogTime = currentTime;
}
averageCounter++;
volt_read += analogRead(IS_PIN)*3300/1024-vOffset;
//volt_read += analogReadMilliVolts(IS_PIN)-vOffset;
if (averageCounter == 100) {
volt_read = volt_read/100.0;
// Serial.println(volt_read);
amp_read = volt_read/100.0*5/3.3; // amp read
volt_read = 0;
averageCounter = 0;
}
/* input button control */
//start FLAG_L
if ((digitalRead(LEFT_BTN) == LOW) && !FLAG_L && !FLAG_R) {
motor_left();
}
//start FLAG_R
if ((digitalRead(RIGHT_BTN) == LOW) && !FLAG_R && !FLAG_L) {
motor_right();
}
//light button press
if ((digitalRead(LED_BTN) == LOW) && !FLAG_LED) {
FLAG_LED = true;
ledTime = currentTime;
digitalWrite(LED_BAR,HIGH);// turn on light
Serial.println("Light ON");
SerialBT.println("Light ON");
}
//factory button press
if (digitalRead(FACTORY_RESET_BTN) == LOW) {
if (currentTime - factoryTime > 3000) {
resetEEPROM();
Serial.println("Factory Reset");
SerialBT.println("Factory Reset");
ledcWriteTone(buzzerChannel,1500);
delay(2000);
ESP.restart();//restart
}
} else {
factoryTime = currentTime;
}
/*------------*/
//Motor Stop Control
if (FLAG_L || FLAG_R) {//now turning FLAG_L or FLAG_R
//check overload
if (amp_read > limit_current) {
motor_stop("STOP [overload by motor stuck]");
uint16_t count = EEPROM.readUShort(0x06);
EEPROM.writeUShort(0x06,count+1);
EEPROM.commit();
//do fast beep
FLAG_BEEP = false;
byte i=0;
for (i=0;i<10;i++ ) {
if (FLAG_BEEP)
ledcWriteTone(buzzerChannel,0);
else
ledcWriteTone(buzzerChannel,1500);
FLAG_BEEP = !FLAG_BEEP;
delay(250);
}
Serial.println("Ready!");
SerialBT.println("Ready!");
} else if (currentTime - lastTime < 2000) {//read startup current
steady_current = amp_read;
Serial.printf("%f\n",amp_read);
SerialBT.printf("%f\n",amp_read);
} else if (currentTime - lastTime > 2000) {//steady current after 2 sec
Serial.printf("%f\n",amp_read);
SerialBT.printf("%f\n",amp_read);
if (amp_read > steady_current*((stop_current_threshold+100)/100)) {//normal stop by current reading
motor_stop("STOP [overload by close/open/blocked]");
uint16_t count = EEPROM.readUShort(0x02);
EEPROM.writeUShort(0x02,count+1);
EEPROM.commit();
Serial.println("Ready!");
SerialBT.println("Ready!");
}
//stop by user press any button
if ((digitalRead(LEFT_BTN) == LOW) || (digitalRead(RIGHT_BTN) == LOW)) {
motor_stop("STOP [button]");
uint16_t count = EEPROM.readUShort(0x04);
EEPROM.writeUShort(0x04,count+1);
EEPROM.commit();
Serial.println("wait 2 sec");
SerialBT.println("wait 2 sec");
delay(2000);//prevent to start again
Serial.println("Ready!");
SerialBT.println("Ready!");
}
} //motor run too long
if (currentTime - lastTime > max_runtime) {
lastTime = currentTime;
motor_stop("STOP [timeout]");
uint16_t count = EEPROM.readUShort(0x08);
EEPROM.writeUShort(0x08,count+1);
EEPROM.commit();
Serial.println("Ready!");
SerialBT.println("Ready!");
}
//make FLAG_BEEPing during working
if (currentTime - buzzerTime> 500) {//0.5 sec
buzzerTime = currentTime;
if (FLAG_BEEP)
ledcWriteTone(buzzerChannel,0);
else
ledcWriteTone(buzzerChannel,1500);
FLAG_BEEP = !FLAG_BEEP;
}
}//if (FLAG_R||FLAG_L)
//turn of LED light
if (FLAG_LED && (currentTime - ledTime > 30000)) {//30 sec
FLAG_LED = false;
digitalWrite(LED_BAR,LOW); //turn off led
Serial.println("Light OFF");
SerialBT.println("Light OFF");
}
//remote control with bluetooth
if (SerialBT.available()) {
char incomingChar = SerialBT.read();
//check CR/NL
if ((incomingChar != '\r') && (incomingChar !='\n')) {
message = message + incomingChar;
// digitalWrite(LED_BUILTIN,HIGH);//turn on builtin LED_BUILTIN
} else { //terminate
// digitalWrite(LED_BUILTIN,LOW);//turn off builtin LED_BUILTIN
//Security check first
/*message format <password=command,para1,para2,para3,para4,..>
Command List
"L" : LEFT
"R" : RIGHT
"LED" : Toggle LED bar
"RESTART" : restart board
"REPORT" : report usage status
"FACTORY" : factory reset (hold 3 sec)
"MELODY" : play song
"CONFIG" : set parameter
dutycycle,freq,max_runtime,limit_current,stop_current,voffset
*/
int index = message.indexOf('=');
if ((message.length() > 0) && (index != -1)) {
String pwd = message.substring(0,index);//get passsword
Serial.println(pwd);
//check password
if (pwd != password) {
Serial.println("Wrong password");
message = "";
SerialBT.flush();
} else {
//get command
byte StringCount = 0;
message = message.substring(index+1,message.length());
while (message.length() > 0) {
index = message.indexOf(',');
if (index == -1) {
parameter[StringCount++] = message;
break;
} else {
parameter[StringCount++] = message.substring(0,index);
message = message.substring(index+1);
}
}//while
//function by command
if (parameter[0] == "L") {//LEFT
if (!FLAG_L && !FLAG_R) {motor_left();}
else {
motor_stop("STOP [button]");
uint16_t count = EEPROM.readUShort(0x04);
EEPROM.writeUShort(0x04,count+1);
EEPROM.commit();
Serial.println("wait 2 sec");
SerialBT.println("wait 2 sec");
delay(2000);//prevent to start again
Serial.println("Ready!");
SerialBT.println("Ready!");
}
}
if (parameter[0] == "R") {//RIGHT
if (!FLAG_R && !FLAG_L) {motor_right();}
else {
motor_stop("STOP [button]");
uint16_t count = EEPROM.readUShort(0x04);
EEPROM.writeUShort(0x04,count+1);
EEPROM.commit();
Serial.println("wait 2 sec");
SerialBT.println("wait 2 sec");
delay(2000);//prevent to start again
Serial.println("Ready!");
SerialBT.println("Ready!");
}
}
if (parameter[0] == "LED") {//TURN ON LED
ledTime = currentTime;
FLAG_LED = true;
digitalWrite(LED_BAR,HIGH);//turn on LED light
Serial.println("Light ON");
SerialBT.println("Light ON");
}
if (parameter[0] == "RESTART") {//RESTART
SerialBT.println("OK RESTART");
ledcWriteTone(buzzerChannel,1500);
delay(1000);
ledcWriteTone(buzzerChannel,0);
ESP.restart();
}
if (parameter[0] == "REPORT") {//list usage stat from eeprom
report();
}
if (parameter[0] == "FACTORY") {//FACTORY RESET
resetEEPROM();
SerialBT.println("OK Factory Reset");
ledcWriteTone(buzzerChannel,1500);
delay(1000);
ledcWriteTone(buzzerChannel,0);
ESP.restart();
}
if (parameter[0] == "MELODY") {//PLAYSONG
// doraemon(buzzerChannel);//play doraemon song
}
if (parameter[0] == "CONFIG") {//SET DEFAULT
//dutycycle,freq,max_runtime,limit_current,stop_current,voffset
// Show the resulting substrings
for (int i = 1; i < 7; i++) {
Serial.print(i);
Serial.print(": \"");
Serial.print(parameter[i]);
Serial.println("\"");
}
//save to eeprom
DEFAULT_DutyCycle = parameter[1].toInt();
DEFAULT_freq = parameter[2].toInt();
DEFAULT_max_runtime = parameter[3].toInt();
DEFAULT_limit_current = parameter[4].toDouble();
DEFAULT_stop_current_threshold = parameter[5].toInt();
DEFAULT_vOffset = parameter[6].toInt();
resetEEPROM();
Serial.println("OK RESTART");
SerialBT.println("OK RESTART");
ledcWriteTone(buzzerChannel,1500);
delay(1000);
ledcWriteTone(buzzerChannel,0);
ESP.restart();
}//if config
message = "";
SerialBT.flush();
digitalWrite(LED_BUILTIN,LOW);//turn off builtin LED_BUILTIN
}//else pwd
}//if message.length
}//if incomming char
}//if SerialBT.avai
}//main