An Electric Scooter Community on a Mission to Stamp out Transportation Mediocrity.

Changing mobility one trip at a time.
#19360
sounds really nice
Pata27 wrote:
Thu Jan 23, 2020 1:06 pm
Hi all,
I'm working on a more functions friendly code for ours arduino nano's.
I will send it once it will be tested by some friend of mine to avoid bugs.
It will give you the ability to use a switch or a capacitive sensor to turn on/off the headlight.
Detect powerloss to lock it directly other than waiting 20sec of timeout.
And the possibility to use a speaker.

All those functions are gonna be 'options' so you will have the possiblity to turn them on or off as you want with some variables in the code.

I will work in the future in an implementation of some NFC to unlock the scooter.
If you want some more functions, just reply to this message saying what you want to be available in the code.
Could be somthing like two speed modes or anything else, if you had some ideas i be glad to implement them in the code.
#19362
Zou wrote:
Mon Dec 16, 2019 1:59 pm
Hi all,

I pimped up a bit the source code to have some more functionalities :
- Scooter keeps turned on.
- Light can be turned on and off by adding a toggle switch to the arduino (press switch can be easily implemented).
- Scooter is locked and wheels blocked when power is cut on arduino. The code will detect input power going down and will send lock code before dying. Really handy !

How to cable it :
- Red and black wire from scooter goes to step down buck converter. Add a simple toggle switch on the 42v+ line to be able to cut power to the Arduino.
- Add another toggle switch between 5v on Arduino to A0.
- Connect RX and TX as explained before.
- Connect blue cable to the D2 on Arduino.

Everything has been tested (I don't know the model), speed is very good and acceleration also ! Will try on other models if possible.

Evolutions :
- More fluid startup, for now the scooter try hardly to start and succeed after some times
- Unlock speed (need somebody to find the hex code to send :)
- Boost acceleration (if possible ?)
- Bluetooth lock / unlock
- Any idea ?

Here is the source code, explained so everybody can add or modify code easily :
Please share !
Code: Select all
#include <Arduino.h>

// ------------------------------------------------------------------------------------------- SETTINGS

// Digital output which send voltage to the brain
int powerPin = 2;

// Analog pin where is connected the light switch
int lightSwitchPin = 0;

// Threshold to detect shutdown and send lock command before turning off (in mV)
int vinShutdownVoltage = 3600;

// Voltage to consider a switch on for the light (in V)
int lightOnVoltage = 2;

// ------------------------------------------------------------------------------------------- HELPERS

/**
 * Read VIN voltage
 */
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
}

/**
 * Quick blink of the internal LED
 */
void blink ()
{
  for (int i = 0; i < 8; i ++)
  {
    delay( 40 );
    digitalWrite(LED_BUILTIN, (i % 2 == 0) ? HIGH : LOW);
  }
}

// ------------------------------------------------------------------------------------------- COMMANDS
// Commands sent to the brain

void sendStartCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x11, 0x14, 0x0B};
  Serial.write( command , sizeof(command) );
}
void sendLightCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x15, 0x14, 0x30};
  Serial.write( command , sizeof(command) );
}
void sendStopCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x10, 0x14, 0xCF};
  Serial.write( command , sizeof(command) );
}

// ------------------------------------------------------------------------------------------- SETUP LIFECYCLE

// Counter for each loop to send commands with smaller delays
int stepCounter = -1;

// Counter to allow shutdown only after everything is up
int allowShutdownCounter = 0;

// Light state (true is on)
bool lightState = false;


void setup()
{
  // We speak serial with the brain
  Serial.begin( 9600 );

  // Configure IO
  pinMode( LED_BUILTIN, OUTPUT );

  // Enable digital pin to talk to the brain
  pinMode(powerPin, OUTPUT);
  digitalWrite(powerPin, HIGH);

  // Start
  sendStartCommand();
  delay(100);
  blink();
}

// ------------------------------------------------------------------------------------------- LOOP LIFECYCLE

void loop ()
{
  // --- Light state and repeat to keep alive
  
  // Counter to allow faster turn off state
  stepCounter ++;
  
  // Read analog value for light switch
  int lightLevel = analogRead( lightSwitchPin );
  float lightVoltage = lightLevel * (5.0 / 1024.0);

  // Convert voltage to boolean, is light switch on ?
  bool newLightState = ( lightVoltage > lightOnVoltage );

  // If light switch state is different than light state
  // Or if we elapsed some time
  if (
      // This is to avoid flooding and only send when state changes
      ( newLightState != lightState && stepCounter >= 50 )
      
      // This is because we need to send last command to avoid the brain to sleep
      || stepCounter >= 500

      // First init of light state
      || stepCounter == -1
  ) {
    // Reset step counter and update light state
    stepCounter = 0;
    lightState = newLightState;
    
    // If voltage is near 5V, turn on light and start the engine
    lightState ? sendLightCommand()
    
    // Otherwise, just start the engine and keep it on
    : sendStartCommand();
  }

  // --- Shutdown detection

  // Set a counter to allow shutdown. This is to avoid shutdown detection at startup
  if ( allowShutdownCounter < 200 ) allowShutdownCounter ++;
  
  // Read VIN value and if voltage is droping
  while ( readVcc() < vinShutdownVoltage && allowShutdownCounter >= 200 )
  {
    // We tell the brain to stop
    sendStopCommand();
    digitalWrite(powerPin, LOW);

    // Blink until death
    while (true) blink();
  }

  // --- Repeat

  // Wait a bit to avoid flooding the brain
  // With this low delay we can detect voltage drop in VIN and lock before we turned off
  // +2ms in readVcc()
  delay( 3 ); // 5ms
}
hey Zou, this is amazing! thank you! i took your code and created my own using your commands, link below. i have yet to fully test the code yet so use at your own risk. i found two commands that give me 20MPH with me 200B with light on and with light off. worst case just steal those commands :)

my code is setup right now so D2 on the nano is the light switch (5V = on, 0V = off), and i borrowed your genius method of reading the 5V supply to send the shutdown command (supply is read on A0). i will be testing this tomorrow once my buck converters come in and will post an update.

code:
https://create.arduino.cc/editor/Cheedo ... f2/preview

Buck Converter I use:
https://www.amazon.com/gp/product/B016V ... UTF8&psc=1

hope this helps somebody!
#19373
Cheedo wrote:
Fri Jan 24, 2020 1:10 pm
Zou wrote:
Mon Dec 16, 2019 1:59 pm
Hi all,

I pimped up a bit the source code to have some more functionalities :
- Scooter keeps turned on.
- Light can be turned on and off by adding a toggle switch to the arduino (press switch can be easily implemented).
- Scooter is locked and wheels blocked when power is cut on arduino. The code will detect input power going down and will send lock code before dying. Really handy !

How to cable it :
- Red and black wire from scooter goes to step down buck converter. Add a simple toggle switch on the 42v+ line to be able to cut power to the Arduino.
- Add another toggle switch between 5v on Arduino to A0.
- Connect RX and TX as explained before.
- Connect blue cable to the D2 on Arduino.

Everything has been tested (I don't know the model), speed is very good and acceleration also ! Will try on other models if possible.

Evolutions :
- More fluid startup, for now the scooter try hardly to start and succeed after some times
- Unlock speed (need somebody to find the hex code to send :)
- Boost acceleration (if possible ?)
- Bluetooth lock / unlock
- Any idea ?

Here is the source code, explained so everybody can add or modify code easily :
Please share !
Code: Select all
#include <Arduino.h>

// ------------------------------------------------------------------------------------------- SETTINGS

// Digital output which send voltage to the brain
int powerPin = 2;

// Analog pin where is connected the light switch
int lightSwitchPin = 0;

// Threshold to detect shutdown and send lock command before turning off (in mV)
int vinShutdownVoltage = 3600;

// Voltage to consider a switch on for the light (in V)
int lightOnVoltage = 2;

// ------------------------------------------------------------------------------------------- HELPERS

/**
 * Read VIN voltage
 */
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
}

/**
 * Quick blink of the internal LED
 */
void blink ()
{
  for (int i = 0; i < 8; i ++)
  {
    delay( 40 );
    digitalWrite(LED_BUILTIN, (i % 2 == 0) ? HIGH : LOW);
  }
}

// ------------------------------------------------------------------------------------------- COMMANDS
// Commands sent to the brain

void sendStartCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x11, 0x14, 0x0B};
  Serial.write( command , sizeof(command) );
}
void sendLightCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x15, 0x14, 0x30};
  Serial.write( command , sizeof(command) );
}
void sendStopCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x10, 0x14, 0xCF};
  Serial.write( command , sizeof(command) );
}

// ------------------------------------------------------------------------------------------- SETUP LIFECYCLE

// Counter for each loop to send commands with smaller delays
int stepCounter = -1;

// Counter to allow shutdown only after everything is up
int allowShutdownCounter = 0;

// Light state (true is on)
bool lightState = false;


void setup()
{
  // We speak serial with the brain
  Serial.begin( 9600 );

  // Configure IO
  pinMode( LED_BUILTIN, OUTPUT );

  // Enable digital pin to talk to the brain
  pinMode(powerPin, OUTPUT);
  digitalWrite(powerPin, HIGH);

  // Start
  sendStartCommand();
  delay(100);
  blink();
}

// ------------------------------------------------------------------------------------------- LOOP LIFECYCLE

void loop ()
{
  // --- Light state and repeat to keep alive
  
  // Counter to allow faster turn off state
  stepCounter ++;
  
  // Read analog value for light switch
  int lightLevel = analogRead( lightSwitchPin );
  float lightVoltage = lightLevel * (5.0 / 1024.0);

  // Convert voltage to boolean, is light switch on ?
  bool newLightState = ( lightVoltage > lightOnVoltage );

  // If light switch state is different than light state
  // Or if we elapsed some time
  if (
      // This is to avoid flooding and only send when state changes
      ( newLightState != lightState && stepCounter >= 50 )
      
      // This is because we need to send last command to avoid the brain to sleep
      || stepCounter >= 500

      // First init of light state
      || stepCounter == -1
  ) {
    // Reset step counter and update light state
    stepCounter = 0;
    lightState = newLightState;
    
    // If voltage is near 5V, turn on light and start the engine
    lightState ? sendLightCommand()
    
    // Otherwise, just start the engine and keep it on
    : sendStartCommand();
  }

  // --- Shutdown detection

  // Set a counter to allow shutdown. This is to avoid shutdown detection at startup
  if ( allowShutdownCounter < 200 ) allowShutdownCounter ++;
  
  // Read VIN value and if voltage is droping
  while ( readVcc() < vinShutdownVoltage && allowShutdownCounter >= 200 )
  {
    // We tell the brain to stop
    sendStopCommand();
    digitalWrite(powerPin, LOW);

    // Blink until death
    while (true) blink();
  }

  // --- Repeat

  // Wait a bit to avoid flooding the brain
  // With this low delay we can detect voltage drop in VIN and lock before we turned off
  // +2ms in readVcc()
  delay( 3 ); // 5ms
}
hey Zou, this is amazing! thank you! i took your code and created my own using your commands, link below. i have yet to fully test the code yet so use at your own risk. i found two commands that give me 20MPH with me 200B with light on and with light off. worst case just steal those commands :)

my code is setup right now so D2 on the nano is the light switch (5V = on, 0V = off), and i borrowed your genius method of reading the 5V supply to send the shutdown command (supply is read on A0). i will be testing this tomorrow once my buck converters come in and will post an update.

code:
https://create.arduino.cc/editor/Cheedo ... f2/preview

Buck Converter I use:
https://www.amazon.com/gp/product/B016V ... UTF8&psc=1

hope this helps somebody!


First: Awesome work!
But just for your interest:
You don't need to send any lock code to the Motorcontroller when switching off the Nano because the wheel will lock automatically when no unlock command is sent to the Motorcontroller. It's a anti theft security feature for sharing scooter ;) (and maybe for private ones, too. I don't have one :D )

Second:
The solution to read the battery status could be made by adding a voltage divider parallel to the buck converter to savely lower the voltage and read it on an analog input. According to this voltage you could assume the percentage ;)
#19389
I did some work because i want to have an OTA option ;)

Here the results:
Code: Select all
// RemoteXY select connection mode and include library 
#define REMOTEXY_MODE__ESP32CORE_BLE

#include <RemoteXY.h>

// RemoteXY connection settings 
#define REMOTEXY_BLUETOOTH_NAME "Your Scooter"
#define REMOTEXY_ACCESS_PASSWORD "password"


// RemoteXY configurate  
#pragma pack(push, 1)
uint8_t RemoteXY_CONF[] =
  { 255,4,0,0,0,155,0,8,8,1,
  2,0,6,16,22,11,4,26,31,31,
  79,78,0,79,70,70,0,129,0,8,
  8,18,6,16,80,111,119,101,114,0,
  2,0,36,18,18,8,1,26,31,31,
  79,78,0,79,70,70,0,129,0,39,
  10,12,6,16,70,97,115,116,0,3,
  3,13,48,9,25,3,26,129,0,14,
  38,15,6,16,76,105,103,104,116,0,
  129,0,27,49,8,6,16,79,110,0,
  129,0,27,58,9,6,16,79,102,102,
  0,129,0,27,66,24,6,16,66,108,
  105,110,107,105,110,103,0,2,0,7,
  89,14,7,14,26,31,31,79,78,0,
  79,70,70,0,129,0,24,90,28,5,
  16,79,84,65,32,85,112,100,97,116,
  101,0 };
  
// this structure defines all the variables of your control interface 
struct {

    // input variable
  uint8_t switch_1; // =1 if switch ON and =0 if OFF 
  uint8_t speed_switch; // =1 if switch ON and =0 if OFF 
  uint8_t select_1; // =0 if select position A, =1 if position B, =2 if position C, ... 
  uint8_t ota_update; // =1 if switch ON and =0 if OFF 

    // other variable
  uint8_t connect_flag;  // =1 if wire connected, else =0 

} RemoteXY;
#pragma pack(pop)


#include <WiFi.h>
#include <WiFiClient.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>


byte slow[] = {0xA6, 0x12, 0x02, 0x35, 0x14, 0xF1};   // Speed Limit to 20 km/h
byte fast[] = {0xA6, 0x12, 0x02, 0x35, 0xFF, 0x38};   // Maximum Speed
byte off[] = {0xA6, 0x12, 0x02, 0x00, 0xFF, 0xEA};
byte fast_stealth[] = {0xA6, 0x12, 0x02, 0x31, 0xFF, 0x03};
byte fast_blinking[] = {0xA6, 0x12, 0x02, 0x33, 0xFF, 0x92};
byte slow_stealth[] = {0xA6, 0x12, 0x02, 0x31, 0x14, 0xCA};
byte slow_blinking[] = {0xA6, 0x12, 0x02, 0x33, 0x14, 0x5B};
const char* host = "esp32";
const char* ssid = "SSID";
const char* password = "PASSWORD";

WebServer server(80);

/*
 * Login page
 */

const char* loginIndex = 
 "<form name='loginForm'>"
    "<table width='20%' bgcolor='A09F9F' align='center'>"
        "<tr>"
            "<td colspan=2>"
                "<center><font size=4><b>ESP32 Login Page</b></font></center>"
                "<br>"
            "</td>"
            "<br>"
            "<br>"
        "</tr>"
        "<td>Username:</td>"
        "<td><input type='text' size=25 name='userid'><br></td>"
        "</tr>"
        "<br>"
        "<br>"
        "<tr>"
            "<td>Password:</td>"
            "<td><input type='Password' size=25 name='pwd'><br></td>"
            "<br>"
            "<br>"
        "</tr>"
        "<tr>"
            "<td><input type='submit' onclick='check(this.form)' value='Login'></td>"
        "</tr>"
    "</table>"
"</form>"
"<script>"
    "function check(form)"
    "{"
    "if(form.userid.value=='admin' && form.pwd.value=='admin')"
    "{"
    "window.open('/serverIndex')"
    "}"
    "else"
    "{"
    " alert('Error Password or Username')/*displays error message*/"
    "}"
    "}"
"</script>";
 
/*
 * Server Index Page
 */
 
const char* serverIndex = 
"<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
   "<input type='file' name='update'>"
        "<input type='submit' value='Update'>"
    "</form>"
 "<div id='prg'>progress: 0%</div>"
 "<script>"
  "$('form').submit(function(e){"
  "e.preventDefault();"
  "var form = $('#upload_form')[0];"
  "var data = new FormData(form);"
  " $.ajax({"
  "url: '/update',"
  "type: 'POST',"
  "data: data,"
  "contentType: false,"
  "processData:false,"
  "xhr: function() {"
  "var xhr = new window.XMLHttpRequest();"
  "xhr.upload.addEventListener('progress', function(evt) {"
  "if (evt.lengthComputable) {"
  "var per = evt.loaded / evt.total;"
  "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
  "}"
  "}, false);"
  "return xhr;"
  "},"
  "success:function(d, s) {"
  "console.log('success!')" 
 "},"
 "error: function (a, b, c) {"
 "}"
 "});"
 "});"
 "</script>";

/*
 * setup function
 */
void setup(void) {
  Serial.begin(9600);
  RemoteXY_Init ();
  

}

void loop(void) {
  
if(RemoteXY.ota_update == 1)
{  
  server.handleClient();
}
if(RemoteXY.ota_update == 0)
{

   RemoteXY_Handler ();
  
   if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 0) && (RemoteXY.select_1 == 0))
   {
    Serial.write(slow, sizeof(slow));    
   }

   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 0) && (RemoteXY.select_1 == 1))
   {
    Serial.write(slow_stealth, sizeof(slow_stealth));
   }

   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 0) && (RemoteXY.select_1 == 2))
   {
    Serial.write(slow_blinking, sizeof(slow_blinking));
   }


   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 1) && (RemoteXY.select_1 == 0))
   {
    Serial.write(fast, sizeof(fast));
   }

   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 1) && (RemoteXY.select_1 == 1))
   {
    Serial.write(fast_stealth, sizeof(fast_stealth));
   }

   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 1) && (RemoteXY.select_1 == 2))
   {
    Serial.write(fast_blinking, sizeof(fast_blinking));
   }
    
   else
   {
      Serial.write(off, sizeof(off));     
   }
   delay(200);
}

if((RemoteXY.ota_update == 1) && (WiFi.status() != WL_CONNECTED))
{
     // Connect to WiFi network
  WiFi.begin(ssid, password);
  Serial.println("");
  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  /*use mdns for host name resolution*/
  if (!MDNS.begin(host)) { //http://esp32.local
    Serial.println("Error setting up MDNS responder!");
    while (1) {
      delay(1000);
    }
  }
  Serial.println("mDNS responder started");
  /*return index page which is stored in serverIndex */
  server.on("/", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", loginIndex);
  });
  server.on("/serverIndex", HTTP_GET, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/html", serverIndex);
  });
  /*handling uploading firmware file */
  server.on("/update", HTTP_POST, []() {
    server.sendHeader("Connection", "close");
    server.send(200, "text/plain", (Update.hasError()) ? "FAIL" : "OK");
    ESP.restart();
  }, []() {
    HTTPUpload& upload = server.upload();
    if (upload.status == UPLOAD_FILE_START) {
      Serial.printf("Update: %s\n", upload.filename.c_str());
      if (!Update.begin(UPDATE_SIZE_UNKNOWN)) { //start with max available size
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_WRITE) {
      /* flashing firmware to ESP*/
      if (Update.write(upload.buf, upload.currentSize) != upload.currentSize) {
        Update.printError(Serial);
      }
    } else if (upload.status == UPLOAD_FILE_END) {
      if (Update.end(true)) { //true to set the size to the current progress
        Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
      } else {
        Update.printError(Serial);
      }
    }
  });
  server.begin();

}
   
    
   
   
}

 

Enjoy ^^

EDIT:
I updated the code but unfortunately the upload of the firmware file doesn't work :(
Maybe somebody has a solution ^^
Last edited by mayaku on Mon Jan 27, 2020 2:16 pm, edited 1 time in total.
#19390
mayaku wrote:
Fri Jan 24, 2020 10:24 pm
Cheedo wrote:
Fri Jan 24, 2020 1:10 pm
Zou wrote:
Mon Dec 16, 2019 1:59 pm
Hi all,

I pimped up a bit the source code to have some more functionalities :
- Scooter keeps turned on.
- Light can be turned on and off by adding a toggle switch to the arduino (press switch can be easily implemented).
- Scooter is locked and wheels blocked when power is cut on arduino. The code will detect input power going down and will send lock code before dying. Really handy !

How to cable it :
- Red and black wire from scooter goes to step down buck converter. Add a simple toggle switch on the 42v+ line to be able to cut power to the Arduino.
- Add another toggle switch between 5v on Arduino to A0.
- Connect RX and TX as explained before.
- Connect blue cable to the D2 on Arduino.

Everything has been tested (I don't know the model), speed is very good and acceleration also ! Will try on other models if possible.

Evolutions :
- More fluid startup, for now the scooter try hardly to start and succeed after some times
- Unlock speed (need somebody to find the hex code to send :)
- Boost acceleration (if possible ?)
- Bluetooth lock / unlock
- Any idea ?

Here is the source code, explained so everybody can add or modify code easily :
Please share !
Code: Select all
#include <Arduino.h>

// ------------------------------------------------------------------------------------------- SETTINGS

// Digital output which send voltage to the brain
int powerPin = 2;

// Analog pin where is connected the light switch
int lightSwitchPin = 0;

// Threshold to detect shutdown and send lock command before turning off (in mV)
int vinShutdownVoltage = 3600;

// Voltage to consider a switch on for the light (in V)
int lightOnVoltage = 2;

// ------------------------------------------------------------------------------------------- HELPERS

/**
 * Read VIN voltage
 */
long readVcc() {
  long result;
  // Read 1.1V reference against AVcc
  ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Convert
  while (bit_is_set(ADCSRA,ADSC));
  result = ADCL;
  result |= ADCH<<8;
  result = 1126400L / result; // Back-calculate AVcc in mV
  return result;
}

/**
 * Quick blink of the internal LED
 */
void blink ()
{
  for (int i = 0; i < 8; i ++)
  {
    delay( 40 );
    digitalWrite(LED_BUILTIN, (i % 2 == 0) ? HIGH : LOW);
  }
}

// ------------------------------------------------------------------------------------------- COMMANDS
// Commands sent to the brain

void sendStartCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x11, 0x14, 0x0B};
  Serial.write( command , sizeof(command) );
}
void sendLightCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x15, 0x14, 0x30};
  Serial.write( command , sizeof(command) );
}
void sendStopCommand ()
{
  byte command[] = {0xA6, 0x12, 0x02, 0x10, 0x14, 0xCF};
  Serial.write( command , sizeof(command) );
}

// ------------------------------------------------------------------------------------------- SETUP LIFECYCLE

// Counter for each loop to send commands with smaller delays
int stepCounter = -1;

// Counter to allow shutdown only after everything is up
int allowShutdownCounter = 0;

// Light state (true is on)
bool lightState = false;


void setup()
{
  // We speak serial with the brain
  Serial.begin( 9600 );

  // Configure IO
  pinMode( LED_BUILTIN, OUTPUT );

  // Enable digital pin to talk to the brain
  pinMode(powerPin, OUTPUT);
  digitalWrite(powerPin, HIGH);

  // Start
  sendStartCommand();
  delay(100);
  blink();
}

// ------------------------------------------------------------------------------------------- LOOP LIFECYCLE

void loop ()
{
  // --- Light state and repeat to keep alive
  
  // Counter to allow faster turn off state
  stepCounter ++;
  
  // Read analog value for light switch
  int lightLevel = analogRead( lightSwitchPin );
  float lightVoltage = lightLevel * (5.0 / 1024.0);

  // Convert voltage to boolean, is light switch on ?
  bool newLightState = ( lightVoltage > lightOnVoltage );

  // If light switch state is different than light state
  // Or if we elapsed some time
  if (
      // This is to avoid flooding and only send when state changes
      ( newLightState != lightState && stepCounter >= 50 )
      
      // This is because we need to send last command to avoid the brain to sleep
      || stepCounter >= 500

      // First init of light state
      || stepCounter == -1
  ) {
    // Reset step counter and update light state
    stepCounter = 0;
    lightState = newLightState;
    
    // If voltage is near 5V, turn on light and start the engine
    lightState ? sendLightCommand()
    
    // Otherwise, just start the engine and keep it on
    : sendStartCommand();
  }

  // --- Shutdown detection

  // Set a counter to allow shutdown. This is to avoid shutdown detection at startup
  if ( allowShutdownCounter < 200 ) allowShutdownCounter ++;
  
  // Read VIN value and if voltage is droping
  while ( readVcc() < vinShutdownVoltage && allowShutdownCounter >= 200 )
  {
    // We tell the brain to stop
    sendStopCommand();
    digitalWrite(powerPin, LOW);

    // Blink until death
    while (true) blink();
  }

  // --- Repeat

  // Wait a bit to avoid flooding the brain
  // With this low delay we can detect voltage drop in VIN and lock before we turned off
  // +2ms in readVcc()
  delay( 3 ); // 5ms
}
hey Zou, this is amazing! thank you! i took your code and created my own using your commands, link below. i have yet to fully test the code yet so use at your own risk. i found two commands that give me 20MPH with me 200B with light on and with light off. worst case just steal those commands :)

my code is setup right now so D2 on the nano is the light switch (5V = on, 0V = off), and i borrowed your genius method of reading the 5V supply to send the shutdown command (supply is read on A0). i will be testing this tomorrow once my buck converters come in and will post an update.

code:
https://create.arduino.cc/editor/Cheedo ... f2/preview

Buck Converter I use:
https://www.amazon.com/gp/product/B016V ... UTF8&psc=1

hope this helps somebody!


First: Awesome work!
But just for your interest:
You don't need to send any lock code to the Motorcontroller when switching off the Nano because the wheel will lock automatically when no unlock command is sent to the Motorcontroller. It's a anti theft security feature for sharing scooter ;) (and maybe for private ones, too. I don't have one :D )

Second:
The solution to read the battery status could be made by adding a voltage divider parallel to the buck converter to savely lower the voltage and read it on an analog input. According to this voltage you could assume the percentage ;)
Thanks Mayaku! Yeah I think the problem is the 5V cuts out so fast that the Arduino doesn’t have a chance to send that last command before cutting out. I know I don’t technically “have” to send the command but I like the idea of the scooter turning off immediately. FYI those power supplies died so don’t use them! I’m getting some LM2560’s today and I found another of these supplies rated for 50V which I may also test. Hopefully I can get my scoot up and running again!
#19391
Possibly I killed my ESC100 :( :? :'(

I used the code I indicate below.
#include <Arduino.h>

int PIN_SWITCH_1 = 13;
int powerPin = LED_BUILTIN;
byte messageOff[] = {0xA6, 0x12, 0x02, 0x90, 0x01, 0x42};
byte messageA[] = {0xA6, 0x12, 0x02, 0x10, 0x14, 0xCF};
byte messageC[] = {0xA6, 0x00, 0x00, 0xF5, 0xFF, 0xFC};

void setup() {
Serial.begin(9600);

pinMode (PIN_SWITCH_1, OUTPUT);
pinMode(powerPin, OUTPUT);
digitalWrite(powerPin, HIGH);

Serial.write(messageOff, sizeof(messageOff));
delay(500);
Serial.write(messageC, sizeof(messageC));

}

void loop() {
delay(500);
Serial.write(messageC, sizeof(messageC));
//Serial.write(messageC, sizeof(messageC));
}
It was in tests and everything was going well, display showing speed, lights and wheel unlocked.
but i had a problem with the arduino power cable and it turned off, after a few seconds the scooter too.

Since then I haven't been able to connect anything to the scooter, I removed the 30A fuse and when I try to connect it again I can't, it melted the "terminals" of the fuse.

I measured the battery seems to be normal 42 Volt's output.

But whenever I try to connect a fuse it seems to have a lot of amperage, I had already removed the fuse more times without ever having this problem.

Did my ESC100 die?
#19432
The OTA Update is finally working!!! :mrgreen: :mrgreen: :mrgreen:
Code: Select all
// RemoteXY select connection mode and include library 
#define REMOTEXY_MODE__ESP32CORE_BLE

#include <RemoteXY.h>

// RemoteXY connection settings 
#define REMOTEXY_BLUETOOTH_NAME "Your Scooter"
#define REMOTEXY_ACCESS_PASSWORD "password"


// RemoteXY configurate  
#pragma pack(push, 1)
uint8_t RemoteXY_CONF[] =
  { 255,4,0,0,0,155,0,8,8,1,
  2,0,6,16,22,11,4,26,31,31,
  79,78,0,79,70,70,0,129,0,8,
  8,18,6,16,80,111,119,101,114,0,
  2,0,36,18,18,8,1,26,31,31,
  79,78,0,79,70,70,0,129,0,39,
  10,12,6,16,70,97,115,116,0,3,
  3,13,48,9,25,3,26,129,0,14,
  38,15,6,16,76,105,103,104,116,0,
  129,0,27,49,8,6,16,79,110,0,
  129,0,27,58,9,6,16,79,102,102,
  0,129,0,27,66,24,6,16,66,108,
  105,110,107,105,110,103,0,2,0,7,
  89,14,7,14,26,31,31,79,78,0,
  79,70,70,0,129,0,24,90,28,5,
  16,79,84,65,32,85,112,100,97,116,
  101,0 };
  
// this structure defines all the variables of your control interface 
struct {

    // input variable
  uint8_t switch_1; // =1 if switch ON and =0 if OFF 
  uint8_t speed_switch; // =1 if switch ON and =0 if OFF 
  uint8_t select_1; // =0 if select position A, =1 if position B, =2 if position C, ... 
  uint8_t ota_update; // =1 if switch ON and =0 if OFF 

    // other variable
  uint8_t connect_flag;  // =1 if wire connected, else =0 

} RemoteXY;
#pragma pack(pop)

/**
   httpUpdate.ino

    Created on: 27.11.2015

*/

#include <Arduino.h>

#include <WiFi.h>
#include <WiFiMulti.h>
#include <ArduinoOTA.h>
#include <HTTPClient.h>
#include <HTTPUpdate.h>
const char* ssid = "SSID";
const char* password = "PASSWORD";


byte slow[] = {0xA6, 0x12, 0x02, 0x35, 0x14, 0xF1};   // Speed Limit to 20 km/h
byte fast[] = {0xA6, 0x12, 0x02, 0x35, 0xFF, 0x38};   // Maximum Speed
byte off[] = {0xA6, 0x12, 0x02, 0x00, 0xFF, 0xEA};
byte fast_stealth[] = {0xA6, 0x12, 0x02, 0x31, 0xFF, 0x03};
byte fast_blinking[] = {0xA6, 0x12, 0x02, 0x33, 0xFF, 0x92};
byte slow_stealth[] = {0xA6, 0x12, 0x02, 0x31, 0x14, 0xCA};
byte slow_blinking[] = {0xA6, 0x12, 0x02, 0x33, 0x14, 0x5B};

int run_once = 0;

void setup() {
  Serial.begin(9600);
  RemoteXY_Init ();
  
}

void loop() {
    ArduinoOTA.handle();
   if((RemoteXY.ota_update == 1) && (run_once == 0))
   {
    
      Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }

  // Port defaults to 8266
  // ArduinoOTA.setPort(8266);

  // Hostname defaults to esp8266-[ChipID]
  ArduinoOTA.setHostname("Your Scooter");

  // No authentication by default
  ArduinoOTA.setPassword((const char *)"password");

  ArduinoOTA.onStart([]() {
    Serial.println("Start");
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR) Serial.println("End Failed");
  });
  ArduinoOTA.begin();
  Serial.println("Ready");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  run_once = 1;
   }

   
  if((RemoteXY.ota_update == 0) && (run_once == 0))
{

   RemoteXY_Handler ();
  
   if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 0) && (RemoteXY.select_1 == 0))
   {
    Serial.write(slow, sizeof(slow));    
   }

   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 0) && (RemoteXY.select_1 == 1))
   {
    Serial.write(slow_stealth, sizeof(slow_stealth));
   }

   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 0) && (RemoteXY.select_1 == 2))
   {
    Serial.write(slow_blinking, sizeof(slow_blinking));
   }


   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 1) && (RemoteXY.select_1 == 0))
   {
    Serial.write(fast, sizeof(fast));
   }

   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 1) && (RemoteXY.select_1 == 1))
   {
    Serial.write(fast_stealth, sizeof(fast_stealth));
   }

   else if((RemoteXY.switch_1 == 1) && (RemoteXY.speed_switch == 1) && (RemoteXY.select_1 == 2))
   {
    Serial.write(fast_blinking, sizeof(fast_blinking));
   }
    
   else
   {
      Serial.write(off, sizeof(off));     
   }
   delay(200);
}
 

}
The ESP32 is connecting to a SSID when turning the OTA switch on. If the specified SSID is not present the ESP32 will reboot. If connected you can upload your sketch through the Arduino IDE over the new port ;)
#19434
PTbig wrote:
Sun Jan 26, 2020 3:15 pm
Possibly I killed my ESC100 :( :? :'(

I used the code I indicate below.
#include <Arduino.h>

int PIN_SWITCH_1 = 13;
int powerPin = LED_BUILTIN;
byte messageOff[] = {0xA6, 0x12, 0x02, 0x90, 0x01, 0x42};
byte messageA[] = {0xA6, 0x12, 0x02, 0x10, 0x14, 0xCF};
byte messageC[] = {0xA6, 0x00, 0x00, 0xF5, 0xFF, 0xFC};

void setup() {
Serial.begin(9600);

pinMode (PIN_SWITCH_1, OUTPUT);
pinMode(powerPin, OUTPUT);
digitalWrite(powerPin, HIGH);

Serial.write(messageOff, sizeof(messageOff));
delay(500);
Serial.write(messageC, sizeof(messageC));

}

void loop() {
delay(500);
Serial.write(messageC, sizeof(messageC));
//Serial.write(messageC, sizeof(messageC));
}
It was in tests and everything was going well, display showing speed, lights and wheel unlocked.
but i had a problem with the arduino power cable and it turned off, after a few seconds the scooter too.

Since then I haven't been able to connect anything to the scooter, I removed the 30A fuse and when I try to connect it again I can't, it melted the "terminals" of the fuse.

I measured the battery seems to be normal 42 Volt's output.

But whenever I try to connect a fuse it seems to have a lot of amperage, I had already removed the fuse more times without ever having this problem.

Did my ESC100 die?
And yes, you killed your ESP. I did the same one day i was too stoned XD (Connected full VCC to the chassis, but could revive it with the chip of a Lime ESP i had here. Unfortunately the LCD isn't working but at least the throttle is working again XD )
  • 1
  • 56
  • 57
  • 58
  • 59
  • 60
  • 80

As this was a rental version whos overstock was […]

Any one got any info on beryl bikes I seen a few[…]

LH/ TF-100 Style Display.

Hi I recently converted a Bird Zero to a personal […]

How do you operate dash without button? I have[…]