mavic-mini-battery-info/mavic-mini-battery-info.ino

317 lines
9.1 KiB
C++

/*
thanks to
PowerCartel for smart battery routines - https://github.com/PowerCartel/PackProbe
Bodmer for fast TFT library - https://github.com/Bodmer/TFT_ST7735
Alain Aeropic - for BatMan inspiration - https://www.thingiverse.com/thing:4235767
*/
#define VERSION "v1.0"
#include <TFT_ST7735.h> // Graphics and font library for ST7735 driver chip
#include <SPI.h>
#include <Wire.h>
byte deviceAddress = 11;
// Standard and common non-standard Smart Battery commands
#define BATTERY_MODE 0x03
#define TEMPERATURE 0x08
#define VOLTAGE 0x09
#define CURRENT 0x0A
#define RELATIVE_SOC 0x0D
#define ABSOLUTE_SOC 0x0E
#define REMAINING_CAPACITY 0x0F
#define FULL_CHARGE_CAPACITY 0x10
#define TIME_TO_FULL 0x13
#define CHARGING_CURRENT 0x14
#define CHARGING_VOLTAGE 0x15
#define BATTERY_STATUS 0x16
#define CYCLE_COUNT 0x17
#define DESIGN_CAPACITY 0x18
#define DESIGN_VOLTAGE 0x19
#define SPEC_INFO 0x1A
#define MFG_DATE 0x1B
#define SERIAL_NUM 0x1C
#define MFG_NAME 0x20 // String
#define DEV_NAME 0x21 // String
#define CELL_CHEM 0x22 // String
#define MFG_DATA 0x23 // String
#define CELL4_VOLTAGE 0x3C
#define CELL3_VOLTAGE 0x3D
#define CELL2_VOLTAGE 0x3E
#define CELL1_VOLTAGE 0x3F
#define STATE_OF_HEALTH 0x4F
#define DJI_SERIAL 0xD8 // String
#define bufferLen 32
uint8_t i2cBuffer[bufferLen];
TFT_ST7735 tft = TFT_ST7735();
#include "dji_logo-48x48.h"
#define TFT_BACKGROUND 0xD6BB
void setup()
{
Serial.begin(115200);
Wire.begin();
Wire.setClock(100000);
tft.init();
tft.setRotation(0); // portrait
tft.fillScreen(TFT_BLACK);
drawIcon(dji_logo, (tft.width() - Width)/2, (tft.height() - Height)/2-40, Width, Height);
tft.setTextSize(1);
tft.setTextColor(TFT_BACKGROUND, TFT_BLACK);
tft.drawCentreString("Mavic Mini", 64, 68, 2);
tft.drawCentreString("battery info", 64, 90, 1);
tft.drawCentreString(VERSION, 64, 102, 1);
tft.drawCentreString("github.com/czipis", 64, 120, 1);
delay(3000);
tft.fillScreen(TFT_BACKGROUND);
}
uint8_t read_byte()
{
while (1)
{
if (Wire.available())
{
return Wire.read();
}
}
}
int fetchWord(byte func)
{
Wire.beginTransmission(deviceAddress);
Wire.write(func);
Wire.endTransmission(false);
delay(1);// FIX wire bug
Wire.requestFrom(deviceAddress, 2, true);
uint8_t b1 = read_byte();
uint8_t b2 = read_byte();
Wire.endTransmission();
return (int)b1 | ((( int)b2) << 8);
}
uint8_t i2c_smbus_read_block ( uint8_t command, uint8_t* blockBuffer, uint8_t blockBufferLen )
{
uint8_t x, num_bytes;
Wire.beginTransmission(deviceAddress);
Wire.write(command);
Wire.endTransmission(false);
delay(1);
Wire.requestFrom(deviceAddress, blockBufferLen, true);
num_bytes = read_byte();
num_bytes = constrain(num_bytes, 0, blockBufferLen - 2);
for (x = 0; x < num_bytes - 1; x++) { // -1 because x=num_bytes-1 if x<y; last byte needs to be "nack"'d, x<y-1
blockBuffer[x] = read_byte();
}
blockBuffer[x++] = read_byte(); // this will nack the last byte and store it in x's num_bytes-1 address.
blockBuffer[x] = 0; // and null it at last_byte+1
Wire.endTransmission();
return num_bytes;
}
#define BUFF_SIZE 64
void drawIcon(const unsigned short* icon, int16_t x, int16_t y, int8_t width, int8_t height) {
uint16_t pix_buffer[BUFF_SIZE]; // Pixel buffer (16 bits per pixel)
// Set up a window the right size to stream pixels into
tft.setAddrWindow(x, y, x + width - 1, y + height - 1);
// Work out the number whole buffers to send
uint16_t nb = ((uint16_t)height * width) / BUFF_SIZE;
// Fill and send "nb" buffers to TFT
for (int i = 0; i < nb; i++) {
for (int j = 0; j < BUFF_SIZE; j++) {
pix_buffer[j] = pgm_read_word(&icon[i * BUFF_SIZE + j]);
}
tft.pushColors(pix_buffer, BUFF_SIZE);
}
// Work out number of pixels not yet sent
uint16_t np = ((uint16_t)height * width) % BUFF_SIZE;
// Send any partial buffer left over
if (np) {
for (int i = 0; i < np; i++) pix_buffer[i] = pgm_read_word(&icon[nb * BUFF_SIZE + i]);
tft.pushColors(pix_buffer, np);
}
}
void loop()
{
uint8_t length_read = 0;
Serial.print("Manufacturer Name: ");
length_read = i2c_smbus_read_block(MFG_NAME, i2cBuffer, bufferLen);
Serial.write(i2cBuffer, length_read);
Serial.println("");
Serial.print("Device Name: ");
length_read = i2c_smbus_read_block(DEV_NAME, i2cBuffer, bufferLen);
Serial.write(i2cBuffer, length_read);
Serial.println("");
Serial.print("Chemistry ");
length_read = i2c_smbus_read_block(CELL_CHEM, i2cBuffer, bufferLen);
Serial.write(i2cBuffer, length_read);
Serial.println("");
Serial.print("Manufacturer Data: ");
length_read = i2c_smbus_read_block(MFG_DATA, i2cBuffer, bufferLen);
Serial.write(i2cBuffer, length_read);
Serial.println("");
Serial.print("Design Capacity: " );
Serial.println(fetchWord(DESIGN_CAPACITY));
Serial.print("Design Voltage: " );
Serial.println(fetchWord(DESIGN_VOLTAGE));
Serial.print("Manufacture Date (D.M.Y): " );
String formatted_date;
int mdate = fetchWord(MFG_DATE);
int mday = B00011111 & mdate;
int mmonth = mdate >> 5 & B00001111;
int myear = 1980 + (mdate >> 9 & B01111111);
formatted_date += mday;
formatted_date += ".";
formatted_date += mmonth;
formatted_date += ".";
formatted_date += myear;
Serial.println(formatted_date);
int str_len = formatted_date.length() + 1;
char mfg_date[str_len];
formatted_date.toCharArray(mfg_date, str_len);
Serial.print("Serial Number: ");
length_read = i2c_smbus_read_block(DJI_SERIAL, i2cBuffer, bufferLen);
char *djiserial = i2cBuffer;
Serial.println(djiserial);
Serial.print("Specification Info: ");
Serial.println(fetchWord(SPEC_INFO));
Serial.print("Cycle Count: " );
uint8_t cycles = fetchWord(CYCLE_COUNT);
Serial.println(cycles);
Serial.print("Voltage: ");
float voltage = (float)fetchWord(VOLTAGE) / 1000;
Serial.println(voltage);
Serial.print("Full Charge Capacity: " );
Serial.println(fetchWord(FULL_CHARGE_CAPACITY));
Serial.print("Remaining Capacity: " );
Serial.println(fetchWord(REMAINING_CAPACITY));
Serial.print("Relative Charge(%): ");
uint8_t charge = fetchWord(RELATIVE_SOC);
Serial.println(charge);
Serial.print("Absolute Charge(%): ");
Serial.println(fetchWord(ABSOLUTE_SOC));
Serial.print("Minutes remaining for full charge: ");
Serial.println(fetchWord(TIME_TO_FULL));
Serial.print("Cell 1 Voltage: ");
float cell1 = (float)fetchWord(CELL1_VOLTAGE)/1000;
Serial.println(cell1);
Serial.print("Cell 2 Voltage: ");
float cell2 = (float)fetchWord(CELL2_VOLTAGE)/1000;
Serial.println(cell2);
char buffer[5];
String v = dtostrf(voltage, 4, 2, buffer);
String c1 = dtostrf(cell1, 4, 2, buffer);
String c2 = dtostrf(cell2, 4, 2, buffer);
String cells = v + " " + c1 + '/' + c2;
str_len = cells.length() + 1;
char cellsV[str_len];
cells.toCharArray(cellsV, str_len);
Serial.print("State of Health: ");
Serial.println(fetchWord(STATE_OF_HEALTH));
Serial.print("Battery Mode (BIN): 0b");
Serial.println(fetchWord(BATTERY_MODE), BIN);
Serial.print("Battery Status (BIN): 0b");
Serial.println(fetchWord(BATTERY_STATUS), BIN);
Serial.print("Charging Current: ");
Serial.println(fetchWord(CHARGING_CURRENT));
Serial.print("Charging Voltage: ");
Serial.println(fetchWord(CHARGING_VOLTAGE));
Serial.print("Temp: ");
unsigned int tempk = fetchWord(TEMPERATURE);
float temp = tempk / 10.0 - 273.15;
Serial.println(temp);
Serial.print("Current (mA): " );
Serial.println(fetchWord(CURRENT));
Serial.println(".");
drawIcon(dji_logo, (tft.width() - Width)/2, 0, Width, Height);
tft.setTextSize(1);
tft.setTextColor(TFT_BLACK, TFT_BLACK);
tft.drawString("SERIAL", 3, 54, 1);
tft.drawRightString(djiserial, 126, 54, 1);
tft.drawString("PRODUCED", 3, 54+15, 1);
tft.drawRightString(mfg_date, 126, 54+15, 1);
tft.drawString("CYCLES", 3, 54+30, 1);b
tft.drawNumber(cycles, 45, 54+30, 1);
tft.drawString("TEMP", 70, 54+30, 1);
tft.fillRect(102, 54+30 , 25, 10, TFT_BACKGROUND);
tft.drawFloat(temp, 1, 102, 54+30, 1);
tft.drawString("VOLTS", 3, 54+45, 1);
tft.fillRect(40, 54+45 , 87, 10, TFT_BACKGROUND);
tft.drawRightString(cellsV, 126, 54+45, 1);
unsigned int batt_width = 110;
unsigned int batt_height = 20;
tft.drawRect(tft.width()/2 - batt_width/2, 130 , batt_width, batt_height, TFT_BLACK);
tft.drawRect(tft.width()/2 + batt_width/2 - 1 , 130+batt_height/4 , 5, batt_height/2, TFT_BLACK);
uint16_t color = TFT_RED;
if (charge > 25) color = TFT_ORANGE;
if (charge > 50) color = TFT_YELLOW;
if (charge > 75) color = TFT_DARKGREEN;
tft.fillRect(tft.width()/2 - batt_width/2 + 5, 130 + 2 , charge, batt_height-4, color);
String pct = String(charge) + '%';
str_len = pct.length() + 1;
char p[str_len];
pct.toCharArray(p, str_len);
tft.drawString(p,54,132, 2);
delay(1000);
}