BLDC#5 ESC Debugger Hardware

In this document every bit of detail is discussed for future reference purpose. This ESC Debugger module is made for the purpose of operating it in conjunction with the ESC board.

What it can do?

  1. It can send/receive the desired parameters to/from ESC controller through UART.
  2. It can store data on internal EEPROM of Atmega328/ External on-board EEPROM.
  3. It can transfer data to the punchline server/PC using on-board WIFI-module.
  4. It can monitor electrical parameters(voltage, current, power, on time) of ESC controller.
  5. It can measure the speed of E-bike using Hall Effect sensor/ IR sensor.
  6. It can display live data on on-board OLED display.
  7. It can tell the state of the ESC-controller using two different colored LEDs.

Schematics:

Fig- 18650 Cell holder

Fig- Arduino Uno Shield

V0 (A0) is to sense the voltage of on board battery consisting of 3 cells. V1 is to sense the voltage of 48V battery connected to the ESC controller.
Fig- Current Sensor

Fig- ESP Wifi Module

Fig- Hall sensor module for speed sensing

Fig- Memory 

Fig- OLED Display

Fig- PCB layout
In the actual hardware, two different colored LEDs are connected to indicate the status of the debugger hardware. Green LED is connected to the digital pin-4 of the microcontroller and Red LED is connected to the digital pin-3 of the microcontroller.
Fig- Switches
SW1 is power switch for the module. While, SB1 to make the parameter selection and mode is to make the mode selection.
Fig- UART Port

Fig- Voltage Sensing Module

you can find the code developed for ESC debugger project here.

CODE-1: Firmware for working with OLED display

#include <avr/io.h>
#include <SPI.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

float batt3cellVolt=0;

void setup(){
  Serial.begin(115200);
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  sendOLEDWelcomeText();
  setPinModes();
}
void loop(){
  //Serial.println("It's working");
  batt3cellVolt=(analogRead(A0)*5.0)/1024.0;
  //Serial.println((analogRead(A0)*5)>>10);
  blinkLEDs();
  sendToOLED(batt3cellVolt*((1.5+1)/1));
  //OLEDMenu();
}
void sendToOLED(float v) {
  display.clearDisplay();
  display.setTextColor(BLACK, WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.setTextColor(WHITE); 
  display.setTextSize(2); 
 
  display.print(v);display.print(F("v "));//battery capacity
  display.display();
}
void sendOLEDWelcomeText(){
  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(2);             // Normal 1:1 pixel scale
  display.setTextColor(WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F(" Punchline"));
  display.println(F("  Energy"));
  display.println(F(" Pvt. Ltd."));
  display.display();
  delay(1000);
}
void OLEDMenu(){
  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F("0: Debug ESC"));
  display.println(F("1: Config ESC"));
  display.println(F("2: LED Delay"));
  display.display();
  delay(1000);
}
void blinkLEDs(){
  digitalWrite(3,HIGH);
  digitalWrite(4,HIGH);
  delay(1000);
  digitalWrite(3,LOW);
  digitalWrite(4,LOW);
  delay(1000);
}
void setPinModes(){
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
}

Code-2 Firmware for ESC Debugger:

#include <avr/io.h>
#include <SPI.h>
#include <Adafruit_GFX.h>           //Includes core graphics library
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


#include "OneButton.h"
// Setup a new OneButton on pin A1.  
OneButton mode(11, true);
// Setup a new OneButton on pin A2.  
OneButton select(12, true);

typedef enum{DebugESC, ConfigESC, LEDDelay}State;
State modeState = DebugESC;
unsigned int modeSingleClickCount=0;

typedef enum{MainMenu, DbgMenu, CfgMenu, LEDMenu}OLEDState;
OLEDState oledDisplayState = MainMenu;

//float batt3cellVolt=0;

int sensor_pin = 2; // IR sensor connection
unsigned int count=0;
unsigned int rpm=0; // RPM value
uint32_t timeBase=0,elapsedTime=0,prevTime=0, timePresent=0,totalTime=0,lastTotalTime=0;

float distance=0.0f,totalPower=0.0f;
float wheel_dia=0.6096f;//in meters

bool addressOverflown=false;

void setup() {
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  Serial.println("Trip Analysis Module");

  // link the button 1 functions.
  mode.attachClick(click1);
  mode.attachDoubleClick(doubleclick1);

  // link the button 2 functions.
  select.attachClick(click2);
  select.attachDoubleClick(doubleclick2);



  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  sendOLEDWelcomeText();

  startTimer1();
  pinMode(13, OUTPUT);
  pinMode(3,OUTPUT);
  pinMode(4,OUTPUT);
  PORTD &= ~(1<<PORTD4);
  
  pinMode(sensor_pin,INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(sensor_pin), counter, FALLING);
} // setup


// main code here, to run repeatedly: 
void loop() {
  static uint8_t batt3cellVolt=0,current=0,batt48Volt=0;
  static float temp=0.0f;
  
  // You can implement other code in here or just wait a while 
  if(oledDisplayState == MainMenu){
    OLEDMenu();
  }else if(oledDisplayState == DbgMenu){
    batt3cellVolt=(uint8_t)(((analogRead(A0)*5.0f)/1024.0f)*((1.5+1)/1));
    batt48Volt=(uint8_t)(((analogRead(A1)*5.0f)/1024.0f)*((10+1)/1));
    temp = (((analogRead(A2)*5.0f)/1024.0f)-2.51f);
    current= temp < 0?(uint8_t)0:(uint8_t)(temp*15.15151515f);

    // Measure Actual RPM
    static uint32_t filterElapsedTime;
    filterElapsedTime = ((filterElapsedTime << 3)-filterElapsedTime+elapsedTime) >> 3;
    rpm = (filterElapsedTime > 10)? (60000/filterElapsedTime):0;

    totalPower += (filterElapsedTime*batt48Volt*current)/60.0f/60.0f/1000.0f;//to get WH

    totalTime = timeBase/100;
    dbgMenu(batt3cellVolt,batt48Volt,current,rpm,totalTime);
    
    if(totalTime % 2 == 0 && lastTotalTime != totalTime && totalTime > 10){//after 10sec starts storing data
      lastTotalTime = totalTime;
      if(!addressOverflown){
        static int address;
        storeInt(address,rpm);
        address = address + 2;
        storeInt(address,batt48Volt);
        address = address + 2;
        storeInt(address,current);
        address = address + 2;
        storeLastAdd(address);
        //checking if the EEPROM is full: FULL: green LED:ON
        if (address == (EEPROM.length()-8)) {
          PIND |= (1<<PIND4);
          addressOverflown = true;
        }
        //Serial.println(address);
      }
    }
    //store distance, time, totalP
    storeInt(1017,(int)distance);
    storeInt(1019,totalTime);
    storeInt(1021,(int)totalPower);
  }else if(oledDisplayState == CfgMenu){
    cfgMenu();
  }else if(oledDisplayState == LEDMenu){
    ledMenu(20);

    //reading EEPROM values
    static int add;
    while(add < readLastAdd()){
      Serial.print(readInt(add));Serial.print(" ");
      add += 2;
      if(add %6 == 0) Serial.println(" ");
    }
    if(add == readLastAdd()){
      Serial.print("Distance: ");Serial.print(readInt(1017));Serial.println(" m");
      Serial.print("Time: ");Serial.print(readInt(1019));Serial.println(" s");
      Serial.print("totalPower: ");Serial.print(readInt(1021));Serial.println(" wh");
      add += 2;
    }
  }
  
  delay(10);
} // loop

void counter() {
  count++;
  PORTD ^= (1<<PORTD3);
  
  timePresent = millis();
  elapsedTime = timePresent-prevTime;
  prevTime = timePresent;

  distance=distance+3.14f*wheel_dia;
}

// ----- button 1 callback functions

// This function will be called when the button1 was pressed 1 time (and no 2. button press followed).
void click1() {
  //Serial.println("Button 1 click.");
  if(++modeSingleClickCount > 2) modeSingleClickCount=0;
  if(modeSingleClickCount == 0){
    modeState = DebugESC;
  }else if(modeSingleClickCount==1){
    modeState = ConfigESC;
  }else if(modeSingleClickCount == 2){
    modeState = LEDDelay;
  }
} // click1


// This function will be called when the button1 was pressed 2 times in a short timeframe.
void doubleclick1() {
  Serial.println("Button 1 doubleclick.");
} // doubleclick1


// ... and the same for button 2:

void click2() {
  //Serial.println("Button 2 click.");
  if(modeState == DebugESC) oledDisplayState = DbgMenu;
  else if(modeState == ConfigESC) oledDisplayState = CfgMenu;
  else if(modeState == LEDDelay) oledDisplayState = LEDMenu;
  else oledDisplayState = MainMenu;
} // click2


void doubleclick2() {
  //Serial.println("Button 2 doubleclick.");
  oledDisplayState = MainMenu;
} // doubleclick2

void sendOLEDWelcomeText(){
  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(2);             // Normal 1:1 pixel scale
  display.setTextColor(WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F(" Punchline"));
  display.println(F("  Energy"));
  display.println(F(" Pvt. Ltd."));
  display.display();
  delay(1000);
}

void OLEDMenu(){
  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(2);             // Normal 1:1 pixel scale
  display.setTextColor(WHITE);        // Draw white text
  display.setCursor(0,0);             // Start at top-left corner
  display.println(F("0:Dbg ESC"));
  display.println(F("1:Cfg ESC"));
  display.println(F("2:EEPROM"));
  display.setCursor(0,50);
  display.println(F("M:"));
  display.setCursor(26,50);
  if(modeState == DebugESC) display.println(F("0"));
  else if(modeState == ConfigESC) display.println(F("1"));
  else if(modeState == LEDDelay) display.println(F("2"));
  display.setCursor(90,50);
  display.println(F("SEL"));
  display.display();
}
void dbgMenu(uint8_t v12, uint8_t v48,uint8_t curr, uint16_t rpm, uint32_t t){
  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(BLACK,WHITE);        // Draw white text
  display.setCursor(20,0);             // Start at top-left corner
  display.println(F("**Debug Menu**"));

  //making first row
  display.setTextColor(WHITE);
  display.setCursor(0,10); 
  display.setTextSize(2); 
  display.print(v12);
  display.println(F("V"));//battery capacity

  display.setCursor(64,10);
  display.print(rpm);

  //making second row
  //display.setTextColor(WHITE);
  display.setCursor(0,30); 
  display.setTextSize(2); 
  display.print(v48);
  display.println(F("V"));//battery capacity

  display.setCursor(64,30);
  display.print(curr);
  display.println(F("A"));//battery capacity
  
  //making third row
  //display.setTextColor(WHITE);
  display.setCursor(0,50); 
  display.setTextSize(2); 
  display.print((t/60)/60);display.print(F("h"));//hours
  display.print((t/60)%60);display.print(F("m"));//minutes
  display.print(t%60);display.println(F("s"));//seconds
/*
  display.setCursor(64,50);
  display.print(rpm);
  display.println(F("v"));//battery capacity
  */
  display.display();
}
void cfgMenu(){
  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(BLACK,WHITE);        // Draw white text
  display.setCursor(25,0);             // Start at top-left corner
  display.println(F("**Cfg Menu**"));
  display.display();
}

void ledMenu(unsigned int delayTime){
  // Clear the buffer
  display.clearDisplay();
  display.setTextSize(1);             // Normal 1:1 pixel scale
  display.setTextColor(BLACK,WHITE);        // Draw white text
  display.setCursor(25,0);             // Start at top-left corner
  display.println(F("**LED Menu**"));
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.println(F("Reading"));
  display.println(F("EEPROM"));
  display.display();
}

void startTimer1(){
  //set timer1 interrupt at 100Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 100hz increments
  OCR1A = 20000;// = (16*10^6) / (100*8) - 1 (must be < 65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12 and CS10 bits for 1024 prescaler
  TCCR1B |= (1 << CS11);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
}
ISR(TIMER1_COMPA_vect){//timer1 interrupt 100Hz toggles
  timeBase++;//one increment in timeBase is equal to 10ms.
  mode.tick();
  select.tick();
}

void blinkLEDs(){
  digitalWrite(3,HIGH);
  digitalWrite(4,HIGH);
  delay(1000);
  digitalWrite(3,LOW);
  digitalWrite(4,LOW);
  delay(1000);
}

void storeLastAdd(int addVal){
  //storing data to eeprom
    EEPROM.update(1023, addVal);
}
int readLastAdd(){
  //storing data to eeprom
  return EEPROM.read(1023);
}

void storeInt(int baseAdd,int integer){
  unsigned int q=0,r=0;
  q=integer/256;
  r=integer%256;
  EEPROM.update(baseAdd, q);
  EEPROM.update(baseAdd+1, r);
}
int readInt(int baseAdd){
  return (EEPROM.read(baseAdd)*256+EEPROM.read(baseAdd+1));
}

// End

CODE-3 WIFI  module code:

Search for the below line
joinAccessPoint = "AT+CWJAP=\"JioFi3_6C0C0F\",\"Punchl1ne\"";
Don't forget to change access point name and password. In this case access point name is JioFi3_6C0C0F and password is Punchl1ne.
#include "SoftwareSerial.h"

SoftwareSerial ESP8266(2, 3);// RX, TX

//variables to be read from memory,these variables will be configured throught the website link
#define VthU 4.2
#define VthL 2.7

unsigned int UIDM;
unsigned int UIDS;
unsigned int CID;
unsigned int BID;

//variables to be uploaded on server if wifi is available else they will be stored in memory
unsigned int batteryCycle;
unsigned int packetNumber;
float V[13]={0};
float current;
float instPower;
float totalPower;
float timeStamp;
float avgSpeed;

//Server configuration
String server;
String openServerPort;
String joinAccessPoint;

void setup(){
  //verify its a punchline processor by reading the IDs from memory
  
  configureProcessor();
  configureServerStrings();
  configureESP();
}

void loop(){
  Serial.print("Starting server at http port...");
  startServerPort();
  Serial.print("         Sending data to server...");
  sendDataToServer();
  //store all the data to the memory if there is no wifi 
}
void sendDataToServer(){
  packetNumber++;
  //cmd =  "GET /live/data.php?string=asifNazeer12345 HTTP/1.1\r\n";
  String cmd =  "GET /live/data.php?string=BC"+String(batteryCycle)\
                                  +"PN"+String(packetNumber)\
                                  +"UIDM"+String(UIDM)\
                                  +"UIDS"+String(UIDS)\
                                  +"CID"+String(CID)\
                                  +"BID"+String(BID)\
                                  +"V1:"+String(V[0])\
                                  +"V2:"+String(V[1])\
                                  +"V3:"+String(V[2])\
                                  +"V4:"+String(V[3])\
                                  +"V5:"+String(V[4])\
                                  +"V6:"+String(V[5])\
                                  +"V7:"+String(V[6])\
                                  +"V8:"+String(V[7])\
                                  +"V9:"+String(V[8])\
                                  +"V10:"+String(V[9])\
                                  +"V11:"+String(V[10])\
                                  +"V12:"+String(V[11])\
                                  +"V13:"+String(V[12])\
                                  +"C"+String(current)\
                                  +"IP"+String(instPower)\
                                  +"TP"+String(totalPower)\
                                  +"T"+String(timeStamp)\
                                  +"SP"+String(avgSpeed)\
                                  +" HTTP/1.1\r\n"; 
  delay(500);
  cmd += "Host: pdpl.in\r\n\r\n";
  delay(500); 
  ESP8266.print("AT+CIPSEND=1,");
  delay(500);
  while(ESP8266.available()){ESP8266.read();}
  ESP8266.println(cmd.length()); 
  delay(2000);
  if(ESP8266.find(">")) Serial.print("OK....."); else Serial.print("Not OK......");
  while(ESP8266.available()){ESP8266.read();}
  ESP8266.println(cmd);
  delay(2000);
  if(ESP8266.find("SEND OK")) Serial.print("sent!......"); else Serial.print("Not sent!......");
  closeServer();
}
void closeServer(){
  while(ESP8266.available()){ESP8266.read();}
  ESP8266.println("AT+CIPCLOSE=1");
  delay(1000);
  if(ESP8266.find("OK")) Serial.println("Server closed!");else Serial.println("Server not Closed!");
  //else closeServer();
}
void configureServerStrings(){
  server="pdpl.in";
  openServerPort="AT+CIPSTART=1,\"TCP\",\""+String(server)+"\",80";
  //joinAccessPoint="AT+CWJAP=\"@sifNaz\",\"8 character\"";
  joinAccessPoint = "AT+CWJAP=\"JioFi3_6C0C0F\",\"Punchl1ne\"";
}
void startServerPort(){
  delay(2000);
  while(ESP8266.available()){ESP8266.read();}
  ESP8266.println(openServerPort);
  Serial.print(".");
  delay(5000);
  if(ESP8266.find("OK")) Serial.println("Started!"); else Serial.println("Not started");
  //else startServerPort();
}
void configureProcessor(){
  Serial.begin(115200);
  UIDM=1;
  UIDS=2;
  CID=1;
  BID=1;
  //variables to be uploaded on server if wifi is available else they will be stored in memory
  batteryCycle=0;
  packetNumber=0;
  current=2.2;
  instPower=1;
  totalPower=2;
  timeStamp=3;
  avgSpeed=4;
  Serial.println("Processor initialized!");
}
void configureESP(){
  ESP8266.begin(115200); //odpowiednia prędkość dla twojego modułu
  delay(2000);
  
  //if(ESP8266.find("ready")) Serial.println("ESP is ready"); else Serial.println("ESP is not ready");
  
  Serial.print("Resetting the ESP...");
  resetESP();
  
  ESP8266.println("AT+CWMODE=1");
  delay("1000");
  
  ESP8266.println("AT+CIPMUX=1");
  delay("1000");
  
  // Connecting to wifi
  Serial.print("Connecting ESP to wifi...");
  connectESPToWifi();
  Serial.println("ESP initialized");
}

void connectESPToWifi(){
  ESP8266.println(joinAccessPoint);
  delay(4000);
  Serial.print(".");
  if(ESP8266.find("OK")) Serial.println("Connected!");
  else connectESPToWifi();
}

void resetESP(){
  ESP8266.println("AT+RESTORE");
  delay(1000);
  Serial.print(".");
  if(ESP8266.find("OK")) Serial.println("Reset successful"); 
  else{
    resetESP();
  }
}

Steps to work with ESC Debugger module:

  1. Connect 3 cells on the board. 1+ and 1- is written on the board for first cell polarity.
  2. Now measure voltage between 1- and 3+. If cells are perfectly connected, voltage in the multimeter should be somewhere between 8V to 12V.
  3. There is a label 12V on the ESC debugger module near OLED display. Connect it to the 12V input power supply of Arduino Uno. Please use red wire.
  4. Use 2.54mm female larger header to stack with Arduino. 
  5. Solder SW1 on the ESC Debugger board.
  6. Stack this ESC Debugger shield on Arduino Uno and turn power key ON. Is any of LED on Arduino Uno board in ON? YES=proceed : NO=check your connection.
  7. Connect OLED display on ESC Debugger shield using a 2.54mm female header. Remember to match the pin order, because in punchline we have OLEDs with two different pin order, one with (GND, VCC, SCL, SDA) and another with (VCC, GND, SCL, SDA) pin order.
  8. solder 1k, 1.5k resistor, and 100nF capacitor under OLED display.
  9. solder 10k, 1k resistor, and 100nF capacitor near SW1 power switch.
  10. connect RED LED at D3 and GREEN LED at D4.
  11. Connect Arduino Uno with PC using Serial to USB cable and start the Arduino IDE. Burn Code-1 given above into the Arduino Uno microcontroller. Successful code upload? YES=Ther is a voltage of on board battery on OLED screen and RED,GREEN LEDs are also blinking : NO=check your connection.
  12.  solder MODE and SELECT push buttons, one 10k resistor nearest to UART header, and UART, Speed-HS 2.54mm header.
  13. solder ACS712 abd two 100nF capacitors near to it.
  14. Now Burn CODE-2 to utilize all it's already implemented functionalities. It's time to use MODE and SELECT buttons.

Work to do...

  1. Use WIFI module off this board, once it's working check the tracks of WIFI on the board and then plug it to send data from the ESC debugger to the punchline server. Use Code-3 to start working with WIFI alone. Watch some of these tutorials for better understanding.
  2. As both OLED display and External EEPROM uses the same I2C bus, there is a problem when both these modules work together. Try to look into the pull-up resistors, as the OLED is working without any problem, so first remove the OLED and get the External EEPROM working. Now the next task is to make them work together so the more data can be stored in on-board EEPROM.
  3. As this debugger was being used with the Hall Effect sensors, try to do the possible modifications on the board to make this debugger compatible with IR sensor as well.
  4. Talk to this board using UART port. Use serial monitoring software, like putty, serial port utility etc.
  5. Try to develop an algorithm so that this ESC debugger can talk to ESC controller for some parameter configuration. Once this is done, try to configure that parameter using the punchline server.

Comments