Hi all!
I have built an e-scooter from a regular one!
I am now trying to add a current sensor, so that I can measure current draw on the battery. I am not sure how to come up with a reliable and steady way to measure battery capacity left, with voltage and current though. I feel like it's very simple, but I just can't seem to figure it out for some reason .
I do have a version of battery percentage left, miles left, and minutes left, but it is unreliable due to voltage drop when using the motor.
Thanks in advance for any ideas!
My current code:
#include "DFRobot_GDL.h"
#include <XPT2046_Touchscreen.h>
#include <SPI.h>
#include "DFRobot_GNSS.h"
#include "DFRobot_BMI160.h"
#include "DFRobot_AHT20.h"
#include "DFRobot_GR10_30.h"
#include <Adafruit_NeoPixel.h>
#include <EEPROM.h>
#include "settings.h"
//Testing
bool testing = false;
//Voltage Conversion for Speed
bool voltconversion = true;
// PWM properties for motor driver
const int freq = 5000;
const int speedChannel = 0;
const int resolution = 8;
//throttle
int throttleoffvalue = 0;
int throttleonvalue = 4095;
int throttleval = 0;
int lastthrottleval = 0;
//Touch coordinates for calibration
#define touchxmin 290
#define touchxmax 3750
#define touchymin 300
#define touchymax 3700
#define backcolor COLOR_RGB565_WHITE
#define erasecolor backcolor//COLOR_RGB565_LGRAY
#define backgroundcolor COLOR_RGB565_CYAN
//gnss / display
long readgnssinc = 1000;//time between GNSS readings
unsigned long lastreadgnss = 0;
long readdisplayinc = 1000;//time between display updates
unsigned long lastdisplay = 0;
sTim_t utc;
sTim_t date;
sLonLat_t latitude;
sLonLat_t longitude;
double altitude;
uint8_t satelites;
double knots;
double course;
//AHT20
float temperature = 0;
float humidity = 0;
float HI = 0;
//WS2812
#define NUMPIXELS 14
float lastcoursereading = 0;
float coursechangetime = 1000;
float lastcoursereadtime = 0;
float courseturnsignalinc = 10; //degrees of turning for turning signal to turn on
float coursechange = 0;
float lastspeedreading = 0;
float speedchangetime = 1000;
float lastspeedreadtime = 0;
float speedbrakeinc = -0.08;//decrease in speed before brake light turns on
int brake[] = {5,6,7,8,9};
int footlight[] = {10,11,12,13};
unsigned long startmillis = 0;
int animationcountleft = 1;
int animationcountright = 1;
bool leftturnsignal = false;
bool rightturnsignal = false;
float speeddiferrence = 0;
bool headlight = false;
int panimationcount = 0;
bool pup = true;
bool ledsoff = false;
//Password
bool locked = false;//true
uint16_t gestures;
int passwordlength = 3;
#define EEPROM_SIZE passwordlength
int passc = 0;//pass count
//battery
float batteryvolts = 0;
int batteryreadings = 10;//number of readings to average, more for a smoother more reliable response
long readbatteryint = 20;//time in milliseconds between battery readings
unsigned long lastbatreading = 0;
long R1 = 15000;//15k ohms
long R2 = 1000;//1.1k ohms
int curbatread = 0;
int batsum = 0;
#define batteryamps 5
#define batteryvoltage 36 //voltage and amp hours of battery, or uncomment "batterywattage" below instead
//#define batterywattage 180
#define motorwattage 350
#define maxcruisespeed 15 //mph //20
//gyro
float lastgyroreading = 0;
float readgyroint = 1;
float curgyroread = 0;
float gyroreadings = 5;
float gyrosum = 0;
float gyroy = 0;
//current sensor
int currentreadings = 10;//number of readings to average, more for a smoother more reliable response
long readcurrentint = 20;//time in milliseconds between battery readings
unsigned long lastcurreading = 0;
int curcurread = 0;
int cursum = 0;
float current = 0; // the average
float watts = 0;
bool lastebrakeon = false;
unsigned long lastebrakechange = 0;
bool ebrakeon = false;
bool lockbtnon = false;
bool lastcruiseon = false;
unsigned long lastcruisechange = 0;
bool cruiseon = false;
float cruisesetspeed = 0;
int motorvalue = 0;
int previousotherx1 = 0;
int previousothery1 = 0;
int previousotherx2 = 0;
int previousothery2 = 0;
bool stole = false;
float speedlimit = 0.00;
bool lastturnstat = false;
XPT2046_Touchscreen ts(TOUCH_CS);
DFRobot_ILI9341_240x320_HW_SPI screen(/*dc=*/TFT_DC,/*cs=*/TFT_CS,/*rst=*/TFT_RST);
DFRobot_GNSS_I2C gnss(&Wire,GNSS_DEVICE_ADDR);
DFRobot_BMI160 bmi160;
const int8_t i2c_addr = 0x69;
DFRobot_AHT20 aht20;
DFRobot_GR10_30 gr10_30(/*addr = */GR10_30_DEVICE_ADDR, /*pWire = */&Wire);
Adafruit_NeoPixel pixels(NUMPIXELS, LEDPIN, NEO_GRB + NEO_KHZ800);
//WiFiMulti wifiMulti;
//BMI160
struct dat{
float x = 0;
float y = 0;
float z = 0;
};
dat accel;
struct data{
float x = 0;
float y = 0;
float z = 0;
};
data gyro;
float baseyangle = 0;
void setup() {
pinMode(speedpin, OUTPUT);
pinMode(27, INPUT_PULLUP);
pinMode(cruisepin, OUTPUT);
digitalWrite(cruisepin, LOW);
if (voltconversion){
digitalWrite(speedpin, HIGH);
}
else {
digitalWrite(speedpin, LOW);
}
Serial.begin(115200);
pixels.begin();
pixels.clear();
pixels.show();
for (int i=0;i<4;i++){
pixels.setPixelColor(footlight[i], pixels.Color(255,255,0));
pixels.show();
}
screen.begin();
screen.setRotation(3);
pinMode(rpmpin, INPUT);
EEPROM.begin(EEPROM_SIZE);
for (int i=0;i<passwordlength;i++){//read password from EEPROM
password[i] = EEPROM.read(i);
}
pinMode(batterypin, INPUT);
pinMode(throttlepin, INPUT);
pinMode(brakepin, OUTPUT);
pinMode(rpmpin, INPUT);
attachInterrupt(rpmpin, interruptRoutine, FALLING);
ledcSetup(speedChannel, freq, resolution);
ledcAttachPin(speedpin, speedChannel);
ledcWrite(speedChannel, 0);
if(!gnss.begin()){
Serial.println("NO GPS device !");
delay(1000);
}
gnss.enablePower();
gnss.setGnss(eGPS_BeiDou_GLONASS);
gnss.setRgbOn();
if (bmi160.softReset() != BMI160_OK){
Serial.println("bmi160 reset false");
//while(1);
}
if (bmi160.I2cInit(i2c_addr) != BMI160_OK){
Serial.println("bmi160 init false");
//while(1);
}
readAccelGyro();
baseyangle = gyro.y;
//baseyangle = 20;
uint8_t status;
if((status = aht20.begin()) != 0){
Serial.print("AHT20 sensor initialization failed. error status : ");
Serial.println(status);
delay(1000);
}
startGestureSensor();
ts.begin();
ts.setRotation(3);
startDisplay();
}
void loop() {
if ((millis() - lastreadgnss) > readgnssinc){
readGNSS();
lastreadgnss = millis();
}
if (!ledsoff){
handleLeds();
}
else {
pixels.clear();
pixels.show();
}
if (locked){
lockedloop();
if (satelites > 8){
if (knots > 2){
//ifttt_counter++;
}
}
return;
}
if (ts.touched()){
TS_Point p = ts.getPoint();
handleTouch(abs(map(p.x,touchxmax,touchxmin,0,320)),abs(map(p.y,touchymax,touchymin,0,240)));
}
readAccelGyro();
readGyro();
readTempHum();
readCurrent();
readBattery();
readThrottle();
if (!rightturnsignal && !leftturnsignal){
if (lastturnstat){
startDisplay();
lastturnstat = false;
}
if (((millis() - lastdisplay) > readdisplayinc) && (satelites != 0)){
handleSlowDisplay();
lastdisplay = millis();
}
handleFastDisplay();
handleDisplayButtons();
float handleAngleLineValue = (baseyangle*100) - (gyroy*100);
handleAngleLine(handleAngleLineValue);
}
else {
if (rightturnsignal){
screen.fillRoundRect(15,38,290,195,12,backgroundcolor);
screen.setTextSize(14);
screen.setCursor(100,75);
screen.setTextColor(COLOR_RGB565_RED);
screen.print(">");
}
else if (leftturnsignal){
screen.fillRoundRect(15,38,290,195,12,backgroundcolor);
screen.setTextSize(14);
screen.setCursor(100,75);
screen.setTextColor(COLOR_RGB565_RED);
screen.print("<");
}
}
handleGestures();
handleMotordriver();
//handleSpeedLimit();
}
/*void onLockChange() {
if (lock == true){
screen.fillRoundRect(15,50,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(20,70);
screen.setTextColor(COLOR_RGB565_RED);
screen.print("Locked");
screen.fillRoundRect(225,50,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(235,70);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print("E ON");
ebrakeon = true;
locked = true;
}
if (lock == false){
ebrakeon = false;
locked = false;
}
}*/
/*void onMaxspeedChange() {
speedlimit = maxspeed;
}
void handleSpeedLimit(){
if (knots > speedlimit){
throttleval = throttleval/2;
}
}*/
void handleTouch(int x,int y){
Serial.print("Touch X = " + String(x));
Serial.println(" | Touch Y = " + String(y));
if (x >= 215 && x <= 320){
if (y < 45){
changepassword();
}
else if (y >= 45 && y <= 104){
if ((millis() - lastebrakechange) > 400){//wait 1 second between reading button as pressed
if (knots < 5){
ebrakeon = !ebrakeon;
lastebrakechange = millis();
}
}
}
else if (y >= 105 && y <= 160){
if ((millis() - lastcruisechange) > 400){//wait 1 second between reading button as pressed
cruiseon = !cruiseon;
cruisesetspeed = knots;
lastcruisechange = millis();
}
}
}
else if (x >= 5 && x <= 105){
if (y < 45){
changepassword();
}
else if (y >= 45 && y <= 105){
if (!lockbtnon){
lockbtnon = true;
}
else {
screen.setTextSize(2);
screen.setCursor(15,115);
screen.setTextColor(COLOR_RGB565_RED);
screen.print("Unlock with gestures!");//display unlock warning
delay(3000);
screen.fillRect(14, 114, 290, 20, erasecolor);//erase display unlock warning
}
}
}
}
void handleSlowDisplay(){
//satelites
screen.setTextSize(2);
screen.setCursor(287,15);
screen.fillRect(286, 13, 26, 18, erasecolor);//backcolor
if (satelites <= 5){
screen.setTextColor(COLOR_RGB565_RED);
}
else if (satelites > 5 && satelites <= 8){
screen.setTextColor(COLOR_RGB565_ORANGE);
}
else if (satelites > 8){
screen.setTextColor(COLOR_RGB565_GREEN);
}
screen.print(String(satelites));
//Serial.println(satelites);
//speed (knots)
screen.setTextSize(4);
if (knots >= 10){
screen.setCursor(100,62);//118,62
screen.fillRect(92, 58, 132, 37, erasecolor);//117, 57, 90, 30,
}
else {
screen.setCursor(110,62);//118,62
screen.fillRect(100, 58, 105, 37, erasecolor);//117, 57, 90, 30,
}
if (knots <= 1.00){
screen.setTextColor(COLOR_RGB565_BLUE);
}
else if (knots > 1.00 && knots <= 12.00){
screen.setTextColor(COLOR_RGB565_GREEN);
}
else if (knots > 12.00 && knots <= 19.00){
screen.setTextColor(COLOR_RGB565_ORANGE);
}
else {
screen.setTextColor(COLOR_RGB565_RED);
}
screen.print(knots);
//if (knots >= cruisesetspeed+4.00){//reset cruise if current speed goes above cruisesetspeed + 4
// cruiseon = false;
//}
//temperature
screen.setTextSize(2);
screen.setCursor(12,215);
screen.fillRect(11,214,55,17,erasecolor);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print(temperature,1);//temperature with only one decimal
screen.drawCircle(64,209,2,COLOR_RGB565_GREEN);//degrees symbol
//humidity
screen.setTextSize(2);
screen.setCursor(270,215);
screen.fillRect(269,214,40,17,erasecolor);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print(humidity,0);//humidity with no decimals
screen.print("%");//percentage symbol
//battery
screen.setTextSize(2);
screen.setCursor(140,215);
screen.fillRect(139,214,65,17,erasecolor);
float batpercent = map(batteryvolts*10,300,420,0,100); //map battery voltage to a percentage value of 0 - 100
batpercent = constrain(batpercent,0,100);
if (batpercent > 80.0){
screen.setTextColor(COLOR_RGB565_GREEN);
}
else if (batpercent <= 80.0 && batpercent > 50.0){
screen.setTextColor(COLOR_RGB565_DGREEN);
}
else if (batpercent <= 50.0 && batpercent > 25.0){
screen.setTextColor(COLOR_RGB565_YELLOW);
}
else if (batpercent <= 25.0 && batpercent > 12.0){
screen.setTextColor(COLOR_RGB565_ORANGE);
}
else if (batpercent <= 12.0 && batpercent > 5.0){
screen.setTextColor(COLOR_RGB565_RED);
}
else {
screen.setTextColor(COLOR_RGB565_RED);
batterywarning();
}
screen.print(batpercent,0);//battery percentage with no decimals
screen.print("%");//percentage symbol
//dist to empty; for 36V 5AH (180W) that would be (at 20mph) 8 miles or 0.4 hours
float batwatts = 0;
#ifdef batterywattage
batwatts = batterywattage;
#else
batwatts = batteryvoltage * batteryamps;
#endif
float bathours = batwatts / motorwattage;
float batmiles = maxcruisespeed * bathours;
float batmilesleft = map(batteryvolts*10,300,420,0,batmiles*100);//map function cant map floating values
batmilesleft = batmilesleft / 100;
float bathoursleft = map(batteryvolts*10,300,420,0,bathours*100);//map function cant map floating values
bathoursleft = bathoursleft / 100;
float batminutesleft = map(bathoursleft*100,0,100,0,600);//map function cant map floating values
batminutesleft = batminutesleft / 10;
screen.setTextSize(3);
screen.setCursor(75,187);
screen.fillRect(74,186,198,24,erasecolor);
screen.setTextColor(COLOR_RGB565_BLUE);
screen.print(abs(batmilesleft),1);//miles with no decimals
screen.print("m|");//miles symbol
screen.print(abs(batminutesleft),1);//hours with two decimals
screen.print("m");
Serial.print("Battery: ");
Serial.println(batteryvolts);
Serial.print("Current: ");
Serial.println(current);
}
void handleFastDisplay(){
//Angle
screen.setTextSize(2);
screen.setCursor(140,95);
screen.fillRect(139,94,75,17,erasecolor);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print((baseyangle*100)-(gyroy*100),1);//angle with only one decimal
}
void handleDisplayButtons(){
//e-brake button
if (ebrakeon != lastebrakeon){
if (!ebrakeon){
screen.fillRoundRect(220,45,90,60,12,erasecolor);//erase
screen.drawRoundRect(225,50,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(235,70);
screen.setTextColor(COLOR_RGB565_RED);
screen.print("E OFF");
}
else {
screen.fillRoundRect(225,50,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(235,70);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print("E ON");
}
lastebrakeon = ebrakeon;
}
if (!lockbtnon){
screen.fillRoundRect(12,45,90,60,12,erasecolor);//erase
screen.drawRoundRect(15,50,80,50,12,backgroundcolor);
screen.setTextSize(1);
screen.setCursor(25,70);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print("Unlocked");
}
else if (lockbtnon || locked){
screen.fillRoundRect(15,50,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(20,70);
screen.setTextColor(COLOR_RGB565_RED);
screen.print("Locked");
ebrakeon = true;
locked = true;
}
if (cruiseon != lastcruiseon){
if (!cruiseon){
screen.fillRoundRect(220,105,90,60,12,erasecolor);//erase
screen.drawRoundRect(225,110,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(235,130);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print("C OFF");
}
else {
screen.fillRoundRect(225,110,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(235,130);
screen.setTextColor(COLOR_RGB565_ORANGE);
screen.print("C ON");
}
lastcruiseon = cruiseon;
}
}
void handleAngleLine(float angle1){
int x=160,y=140; //center point of the two lines
screen.drawLine(x, y, previousotherx1, previousothery1, erasecolor);//line going right
screen.drawLine(x+1, y+1, previousotherx1+1, previousothery1+1, erasecolor);//double thick line
screen.drawLine(x, y, previousotherx2, previousothery2, erasecolor);//line going left
screen.drawLine(x+1, y+1, previousotherx2+1, previousothery2+1, erasecolor);//double thick line
#define DEG2RAD 0.0174532925
float screenangle1 = 135; //value for right line to make it level
float screenangle2 = 315; //value for left line to make it level; appears to be one line instead of two separate ones
float angle2 = angle1;
int w=100,h=100; //length of each line; one going right and one going left
#define linecolor COLOR_RGB565_NAVY
float cosa1 = cos((screenangle1+angle1) * DEG2RAD), sina1 = sin((screenangle1+angle1) * DEG2RAD);
int otherx1 = x - ((w * cosa1 / 2) - (h * sina1 / 2));
int othery1 = y - ((h * cosa1 / 2) + (w * sina1 / 2));
screen.drawLine(x, y, otherx1, othery1, linecolor);//line going right
screen.drawLine(x+1, y+1, otherx1+1, othery1+1, linecolor);//double thick line
float cosa2 = cos((screenangle2+angle2) * DEG2RAD), sina2 = sin((screenangle2+angle2) * DEG2RAD);
int otherx2 = x - ((w * cosa2 / 2) - (h * sina2 / 2));
int othery2 = y - ((h * cosa2 / 2) + (w * sina2 / 2));
screen.drawLine(x, y, otherx2, othery2, linecolor);//line going left
screen.drawLine(x+1, y+1, otherx2+1, othery2+1, linecolor);//double thick line
previousotherx1 = otherx1;
previousothery1 = othery1;
previousotherx2 = otherx2;
previousothery2 = othery2;
}
void batterywarning(){
}
void changepassword(){
screen.fillScreen(backcolor);
screen.fillScreen(COLOR_RGB565_DGRAY);
screen.fillRoundRect(0, 0, 320, 240, 15, backgroundcolor);
screen.fillRoundRect(5, 5, 310, 230, 15, backcolor);
screen.setTextColor(backgroundcolor);
screen.setTextSize(3);
screen.setCursor(30, 11);
screen.print("Change PASS");
screen.drawFastHLine(10, 35, 300, COLOR_RGB565_GREEN);
screen.drawFastHLine(10, 36, 300, COLOR_RGB565_GREEN);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.setTextSize(1);
screen.setCursor(40, 100);
screen.print("Do three gestures to set new password...");
password[0] = 0;
password[1] = 0;
password[2] = 0;
int gesturepasscount = 0;
redo:
if(gr10_30.getDataReady()){
gestures = gr10_30.getGesturesState();
if(gestures&GESTURE_UP){
Serial.println("Up");
password[gesturepasscount] = GESTURE_UP;
}
if(gestures&GESTURE_DOWN){
Serial.println("Down");
password[gesturepasscount] = GESTURE_DOWN;
}
if(gestures&GESTURE_LEFT){
Serial.println("Left");
password[gesturepasscount] = GESTURE_LEFT;
}
if(gestures&GESTURE_RIGHT){
Serial.println("Right");
password[gesturepasscount] = GESTURE_RIGHT;
}
if(gestures&GESTURE_FORWARD){
password[gesturepasscount] = GESTURE_FORWARD;
}
if(gestures&GESTURE_BACKWARD){
password[gesturepasscount] = GESTURE_BACKWARD;
}
if(gestures&GESTURE_CLOCKWISE){
password[gesturepasscount] = GESTURE_CLOCKWISE;
}
if(gestures&GESTURE_COUNTERCLOCKWISE){
password[gesturepasscount] = GESTURE_COUNTERCLOCKWISE;
}
if(gestures&GESTURE_WAVE){
password[gesturepasscount] = GESTURE_WAVE;
}
if(gestures&GESTURE_HOVER){
password[gesturepasscount] = GESTURE_HOVER;
}
if(gestures&GESTURE_CLOCKWISE_C){
password[gesturepasscount] = GESTURE_CLOCKWISE_C;
}
if(gestures&GESTURE_COUNTERCLOCKWISE_C){
password[gesturepasscount] = GESTURE_COUNTERCLOCKWISE_C;
}
EEPROM.write(gesturepasscount,password[gesturepasscount]);
EEPROM.commit();
gesturepasscount++;
Serial.println("gesturepasscount: " + String(gesturepasscount));
}
if (gesturepasscount != passwordlength){
goto redo;
}
Serial.println("Finished!");
gesturepasscount = 0;
startDisplay();
}
void readThrottle(){
throttleval = map(analogRead(throttlepin),throttleoffvalue,throttleonvalue,0,4095);
//Serial.print("Throttle: ");
//Serial.println(throttleval);
//Serial.println("Throttle: " + String(throttleval));
}
void readBattery(){
if ((millis() - lastbatreading) > readbatteryint){
if (curbatread < batteryreadings){
batsum = batsum + analogRead(batterypin);
curbatread++;
}
else{
float batteryread = batsum/batteryreadings;
float batpinvolt = (batteryread*3.3)/4095;//3.3V and 4095 is the max resolution of ESP32
if (testing){
batpinvolt = 2.2;
}
batteryvolts = batpinvolt*16.55;//(1000/(15000+1000)); //(R2/(R1+R2));//VOUT = VIN*R2/R1+R2 ; classic equation for voltage dividers
curbatread = 0;
batsum = 0;
}
lastbatreading = millis();
}
}
void readGyro(){
/*for (curgyroread=0;curgyroread<gyroreadings;curgyroread++){
gyrosum = gyrosum + gyro.y;
delay(readgyroint);
}
gyroy = gyrosum/gyroreadings;
curgyroread = 0;
gyrosum = 0;*/
if ((millis() - lastgyroreading) > readgyroint){
if (curgyroread < gyroreadings){
gyrosum = gyrosum + gyro.y;
curgyroread++;
}
else{
gyroy = gyrosum/gyroreadings;
curgyroread = 0;
gyrosum = 0;
}
lastgyroreading = millis();
}
}
void handleGestures(){
if(gr10_30.getDataReady()){
gestures = gr10_30.getGesturesState();
if(gestures&GESTURE_UP){
Serial.println("Up");
cruiseon = true;
}
if(gestures&GESTURE_DOWN){
Serial.println("Down");
//if (cruiseon){
// ledsoff = !ledsoff;
//}
cruiseon = false;
}
if(gestures&GESTURE_LEFT){
Serial.println("Left");
if (rightturnsignal){
rightturnsignal = false;
pixels.clear();
pixels.show();
}
else {
leftturnsignal = true;
lastturnstat = true;
}
}
if(gestures&GESTURE_RIGHT){
Serial.println("Right");
if (leftturnsignal){
leftturnsignal = false;
pixels.clear();
pixels.show();
}
else {
rightturnsignal = true;
lastturnstat = true;
}
}
if(gestures&GESTURE_FORWARD){
Serial.println("Forward");
ebrakeon = true;
}
if(gestures&GESTURE_BACKWARD){
Serial.println("Backward");
ebrakeon = false;
}
if(gestures&GESTURE_CLOCKWISE){
Serial.println("Clockwise");
}
if(gestures&GESTURE_COUNTERCLOCKWISE){
Serial.println("Contrarotate");
}
if(gestures&GESTURE_WAVE){
Serial.println("Wave");
}
if(gestures&GESTURE_HOVER){
Serial.println("Hover");
}
if(gestures&GESTURE_CLOCKWISE_C){
Serial.println("Continuous clockwise");
headlight = !headlight;
}
if(gestures&GESTURE_COUNTERCLOCKWISE_C){
Serial.println("Continuous counterclockwise");
screen.fillRoundRect(15,50,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(20,70);
screen.setTextColor(COLOR_RGB565_RED);
screen.print("Locked");
screen.fillRoundRect(225,50,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(235,70);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print("E ON");
ebrakeon = true;
locked = true;
}
}
}
void lockedloop(){
if(gr10_30.getDataReady()){
gestures = gr10_30.getGesturesState();
Serial.print("Gesture: ");
Serial.println(gestures);
if (gestures&password[passc]){
Serial.println("Correct!");
Serial.println(password[passc]);
passc++;
Serial.println("PASSC: " + String(passc));
}
else {
Serial.println("Wrong");
passc=0;
}
}
if (passc >= passwordlength){
Serial.println("Unlocked!");
passc=0;
locked = false;
//lock = false;
}
}
void readGNSS(){
utc = gnss.getUTC();
date = gnss.getDate();
latitude = gnss.getLat();
longitude = gnss.getLon();
altitude = gnss.getAlt();
satelites = gnss.getNumSatUsed();
knots = gnss.getSog();
course = gnss.getCog();
}
void readAccelGyro(){
int i = 0;
int rslt;
int16_t accelGyro[6]={0};
//get both accel and gyro data from bmi160
//parameter accelGyro is the pointer to store the data
rslt = bmi160.getAccelGyroData(accelGyro);
if(rslt == 0){
for(i=0;i<6;i++){
if (i<3){
//the first three are gyro data
//Serial.print(accelGyro[i]*3.14/180.0);Serial.print("\t");
if (i==0){accel.x = accelGyro[i]*3.14/180.0;}
else if (i==1){accel.y = accelGyro[i]*3.14/180.0;}
else if (i==2){accel.z = accelGyro[i]*3.14/180.0;}
}else{
//the following three data are accel data
//Serial.print(accelGyro[i]/16384.0);Serial.print("\t");
if (i==3){gyro.x = accelGyro[i]/16384.0;}
else if (i==4){gyro.y = accelGyro[i]/16384.0;}
else if (i==5){gyro.z = accelGyro[i]/16384.0;}
}
}
//Serial.println();
}else{
Serial.println("err");
}
}
void readTempHum(){
if(aht20.startMeasurementReady(/* crcEn = */true)){
temperature = aht20.getTemperature_F();
humidity = aht20.getHumidity_RH();
}
HI = 0.5 * (temperature + 61.0 + ((temperature-68.0)*1.2) + (humidity*0.094));
if (HI > 80.0){
HI = -42.379 + 2.04901523*temperature + 10.14333127*humidity - .22475541*temperature*humidity - .00683783*temperature*temperature - .05481717*humidity*humidity + .00122874*temperature*temperature*humidity + .00085282*temperature*humidity*humidity - .00000199*temperature*temperature*humidity*humidity;
if ((temperature > 80.0 && temperature < 112.0) && (humidity < 13.0)){
HI = ((13-humidity)/4)*sqrt((17-abs(temperature-95.0))/17);
}
else if ((temperature > 80.0 && temperature < 87.0) && (humidity > 85.0)){
HI = ((humidity-85)/10) * ((87-temperature)/5);
}
}
temperature = HI;
}
void readCurrent(){
if ((millis() - lastcurreading) > readcurrentint){
if (curcurread < currentreadings){
cursum = cursum + analogRead(currentpin);
curcurread++;
}
else{
float currentread = cursum/currentreadings;
float curpinvolt = (currentread*3.3)/4095;//3.3V and 4095 is the max resolution of ESP32
current = (curpinvolt-1.65)/0.04;
current = constrain(current,0,50);
watts = current*batteryvolts;
watts = constrain(watts,0,500);
curcurread = 0;
cursum = 0;
}
lastcurreading = millis();
}
//Data processing:510-raw data from analogRead when the input is 0;
// 5-5v; the first 0.04-0.04V/A(sensitivity); the second 0.04-offset val;
}
void handleMotordriver(){
//digitalWrite(cruisepin,cruiseon);
digitalWrite(brakepin,ebrakeon);
if (voltconversion){
motorvalue = map(throttleval,3500,0,0,255);
motorvalue = constrain(motorvalue,0,255);
}
else{
motorvalue = map(throttleval,300,4000,0,255);
motorvalue = constrain(motorvalue,0,255);
}
motorvalue = map(motorvalue,255,0,0,255);
if (motorvalue > 245){
digitalWrite(cruisepin, HIGH);
}
else {
digitalWrite(cruisepin, LOW);
}
if (motorvalue >= 200 && motorvalue <= 245){
if (abs(motorvalue - lastthrottleval) <= 5){
motorvalue = lastthrottleval;
}
else {
lastthrottleval = motorvalue;
}
}
else if (motorvalue < 200 && motorvalue > 110){
if (abs(motorvalue - lastthrottleval) <= 15){
motorvalue = lastthrottleval;
}
else {
lastthrottleval = motorvalue;
}
}
else if (motorvalue <= 110 && motorvalue > 5){
motorvalue = map(motorvalue,5,120,32,87);
}
if (motorvalue >= 250){motorvalue = 255;} else if (motorvalue <= 5){motorvalue = 0;}
if ((lastthrottleval - motorvalue) <= -7){
motorvalue = lastthrottleval - 8;
lastthrottleval = motorvalue;
}
motorvalue = abs(motorvalue);
motorvalue = constrain(motorvalue,0,255);
if (throttleval == 0){
motorvalue = 0;
}
//Serial.print("Motor val: ");
//Serial.print(motorvalue); Serial.print(" | ");
motorvalue = map(motorvalue,0,255,255,0);
ledcWrite(speedChannel, motorvalue);//pwm signal for speed
}
void handleLeds(){
if ((millis() - lastcoursereadtime) > coursechangetime){//handle turning signal
coursechange = course - lastcoursereading;
lastcoursereading = course;
lastcoursereadtime = millis();
}
if (leftturnsignal == true || rightturnsignal == true){
if (leftturnsignal){
//brakelight(3);
leftblinker();
}
if (rightturnsignal){
//brakelight(3);
rightblinker();
}
}
else if (leftturnsignal == false && rightturnsignal == false){
if (abs(coursechange) > courseturnsignalinc){
if (coursechange < 0){
//brakelight(3);
leftblinker();
}
else {
//brakelight(3);
rightblinker();
}
}
else {
if (speeddiferrence >= speedbrakeinc){
//pixels.clear();
//pixels.show();
brakelight(2);
}
}
}
if ((millis() - lastspeedreadtime) > speedchangetime){//handle brake light
speeddiferrence = knots - lastspeedreading;
if (speeddiferrence < speedbrakeinc){
if (rightturnsignal){
pixels.setPixelColor(5, pixels.Color(255,0,0));
pixels.setPixelColor(6, pixels.Color(255,0,0));
pixels.show();
}
else if (leftturnsignal){
pixels.setPixelColor(9, pixels.Color(255,0,0));
pixels.setPixelColor(8, pixels.Color(255,0,0));
pixels.show();
}
else {
brakelight(1);
}
}
//else {
// brakelight(1);
//}
lastspeedreading = knots;
lastspeedreadtime = millis();
}
if (headlight){
headlights(true);
}
else {
headlights(false);
}
handleFootlight();
}
void leftblinker(){
int turningsignalcolor = pixels.Color(0, 20, 255);
if (animationcountleft == 1){
if ((millis() - startmillis) < 250){
pixels.setPixelColor(7, turningsignalcolor);
pixels.show();
}
else {
animationcountleft++;
startmillis = millis();
}
}
else if (animationcountleft == 2){
if ((millis() - startmillis) < 250){
pixels.setPixelColor(7, pixels.Color(0, 0, 0));
pixels.setPixelColor(6, turningsignalcolor);
pixels.show();
}
else {
animationcountleft++;
startmillis = millis();
}
}
if (animationcountleft == 3){
if ((millis() - startmillis) < 750){
pixels.setPixelColor(6, pixels.Color(0, 0, 0));
pixels.setPixelColor(5, turningsignalcolor);
pixels.show();
}
else {
animationcountleft++;
startmillis = millis();
}
}
if (animationcountleft == 4){
if ((millis() - startmillis) < 400){
pixels.setPixelColor(5, pixels.Color(0, 0, 0));
pixels.show();
}
else {
animationcountleft = 1;
startmillis = millis();
}
}
}
void rightblinker(){
int turningsignalcolor = pixels.Color(0, 20, 255);
if (animationcountright == 1){
if ((millis() - startmillis) < 250){
pixels.setPixelColor(7, turningsignalcolor);
pixels.show();
}
else {
animationcountright++;
startmillis = millis();
}
}
else if (animationcountright == 2){
if ((millis() - startmillis) < 250){
pixels.setPixelColor(7, pixels.Color(0, 0, 0));
pixels.setPixelColor(8, turningsignalcolor);
pixels.show();
}
else {
animationcountright++;
startmillis = millis();
}
}
if (animationcountright == 3){
if ((millis() - startmillis) < 750){
pixels.setPixelColor(8, pixels.Color(0, 0, 0));
pixels.setPixelColor(9, turningsignalcolor);
pixels.show();
}
else {
animationcountright++;
startmillis = millis();
}
}
if (animationcountright == 4){
if ((millis() - startmillis) < 400){
pixels.setPixelColor(9, pixels.Color(0, 0, 0));
pixels.show();
}
else {
animationcountright = 1;
startmillis = millis();
}
}
}
void brakelight(int brakelighton){
if (brakelighton == 1){
for (int i=0;i<5;i++){
pixels.setPixelColor(brake[i], pixels.Color(255, 0, 0));//on brake light
}
pixels.show();
}
else if (brakelighton == 2){
for (int i=0;i<5;i++){
pixels.setPixelColor(brake[i], pixels.Color(60, 0, 0));//on brake light
}
pixels.show();
}
else {
for (int i=0;i<5;i++){
pixels.setPixelColor(brake[i], pixels.Color(0, 0, 0));//on brake light
}
pixels.show();
}
}
void headlights(bool on){
if (on){
pixels.setPixelColor(0, pixels.Color(255, 255, 255));//brake light on
pixels.setPixelColor(1, pixels.Color(255, 255, 255));//brake light on
pixels.setPixelColor(2, pixels.Color(255, 255, 255));//brake light on
pixels.setPixelColor(3, pixels.Color(255, 255, 255));//brake light on
pixels.setPixelColor(4, pixels.Color(255, 255, 255));//brake light on
pixels.show();
}
else {
pixels.setPixelColor(0, pixels.Color(0, 0, 0));//dim brake light
pixels.setPixelColor(1, pixels.Color(0, 0, 0));//dim brake light
pixels.setPixelColor(2, pixels.Color(0, 255, 0));//dim brake light
pixels.setPixelColor(3, pixels.Color(0, 0, 0));//dim brake light
pixels.setPixelColor(4, pixels.Color(0, 0, 0));//dim brake light
pixels.show();
}
}
void handleFootlight(){
if (locked){
if (panimationcount > 235 && pup){
pup = false;
}
else if (panimationcount < 0 && !pup){
pup = true;
}
if (pup){
panimationcount+=8;
}
else {
panimationcount-=8;
}
for (int i=0;i<4;i++){
pixels.setPixelColor(footlight[i], pixels.Color(panimationcount,0,0));
pixels.show();
}
//delayMicroseconds(1);
}
else if (knots < 1){
if (panimationcount > 235 && pup){
pup = false;
}
else if (panimationcount < 25 && !pup){
pup = true;
}
if (pup){
panimationcount+=8;
}
else {
panimationcount-=8;
}
for (int i=0;i<4;i++){
pixels.setPixelColor(footlight[i], pixels.Color(0,panimationcount,0));
pixels.show();
}
//delayMicroseconds(1);
}
else {
for (int i=0;i<4;i++){
pixels.setPixelColor(footlight[i], pixels.Color(0,20,255));
pixels.show();
}
}
}
void startDisplay(){
screen.fillScreen(backcolor);
screen.setTextWrap(false);
screen.fillScreen(COLOR_RGB565_DGRAY);
screen.fillRoundRect(0, 0, 320, 240, 15, backgroundcolor);
screen.fillRoundRect(5, 5, 310, 230, 15, backcolor);
//title
screen.setTextColor(backgroundcolor);
screen.setTextSize(3);
screen.setCursor(30, 11);
screen.print("DIY E-Scooter");
screen.drawFastHLine(10, 35, 300, COLOR_RGB565_GREEN);
screen.drawFastHLine(10, 36, 300, COLOR_RGB565_GREEN);
//speed label
screen.setTextColor(COLOR_RGB565_YELLOW);
screen.setTextSize(2);
screen.setCursor(125,40);
screen.print("Speed");
//dist and time to empty labels
screen.setTextColor(COLOR_RGB565_YELLOW);
screen.setTextSize(2);
screen.setCursor(91,170);
screen.print("Dist Time");
//boost button
screen.drawRoundRect(225,50,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(235,70);
screen.setTextColor(COLOR_RGB565_RED);
screen.print("E OFF");
//lock button
screen.drawRoundRect(15,50,80,50,12,backgroundcolor);
screen.setTextSize(1);
screen.setCursor(25,70);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print("Unlocked");
//cruise button
screen.drawRoundRect(225,110,80,50,12,backgroundcolor);
screen.setTextSize(2);
screen.setCursor(235,130);
screen.setTextColor(COLOR_RGB565_GREEN);
screen.print("C OFF");
}
void startGestureSensor(){
if(gr10_30.begin() != 0){
Serial.println(" Sensor initialize failed!!");
delay(1000);
}
/** Set the gesture to be enabled
* GESTURE_UP
* GESTURE_DOWN
* GESTURE_LEFT
* GESTURE_RIGHT
* GESTURE_FORWARD
* GESTURE_BACKWARD
* GESTURE_CLOCKWISE
* GESTURE_COUNTERCLOCKWISE
* GESTURE_WAVE It is not suggested to enable rotation gesture (CW/CCW) and wave gesture at the same time.
* GESTURE_HOVER Disable other gestures when hover gesture enables.
* GESTURE_UNKNOWN
* GESTURE_CLOCKWISE_C
* GESTURE_COUNTERCLOCKWISE_C
*/
gr10_30.enGestures(GESTURE_UP|GESTURE_DOWN|GESTURE_LEFT|GESTURE_RIGHT|GESTURE_FORWARD|GESTURE_BACKWARD|GESTURE_CLOCKWISE|GESTURE_COUNTERCLOCKWISE|GESTURE_CLOCKWISE_C|GESTURE_COUNTERCLOCKWISE_C);
/**
* Set the detection window you want, only data collected in the range are valid
* The largest window is 31, the configured number represents distance from the center to the top, bottom, left and right
* For example, if the configured distance from top to bottom is 30, then the distance from center to top is 15, and distance from center to bottom is also 15
* udSize Range of distance from top to bottom 0-31
* lrSize Range of distance from left to right 0-31
*/
gr10_30.setUdlrWin(30, 30);//0-31,0-31
/**
* Set moving distance that can be recognized as a gesture
* Distance range 5-25, must be less than distances of the detection window
*/
gr10_30.setLeftRange(10);
gr10_30.setRightRange(10);
gr10_30.setUpRange(10);
gr10_30.setDownRange(10);
//gr10_30.setForwardRange(10);
//gr10_30.setBackwardRange(10);
/**
* Set distance of moving forward and backward that can be recognized as a gesture
* Distance range 1-15
*/
gr10_30.setForwardRange(10);
gr10_30.setBackwardRange(10);
/**
* Set rotation angle that can trigger the gesture
* count Default is 16 range 0-31
* count Rotation angle is 22.5 * count
* count = 16 22.5*count = 360 Rotate 360° to trigger the gesture
*/
gr10_30.setCwsAngle(/*count*/16);
gr10_30.setCcwAngle(/*count*/16);
/**
* Set degrees of continuous rotation that can trigger the gesture
* count Default is 4 range 0-31
* count The degrees of continuous rotation is 22.5 * count
* For example: count = 4 22.5*count = 90
* Trigger the clockwise/counterclockwise rotation gesture first, if keep rotating, then the continuous rotation gesture will be triggered once every 90 degrees
*/
gr10_30.setCwsAngleCount(/*count*/8);
gr10_30.setCcwAngleCount(/*count*/8);
}
void interruptRoutine(){
Serial.println("Interrupt...");
}