First build non-hub - quick prototype 750w X1e

steelmesh

100 W
Joined
Jun 28, 2012
Messages
123
Location
Michigan
This is my first ebike build, only experience is youtube. 2x12v agm 9Ah, she goes 4.5 - 8 miles depending on speed and stops, flat land testing. Top speed 21 mph on level ground (no gears yet, welded freewheel). New features just added: cruise, resume, brake cutoff, and amp feedback (using arduino).

I essentially built this in 1-week as a rapid prototype and display model. LOL, I ordered parts Friday, then redesigned the whole motor mount Sunday, finished by Friday. I personally cut the mount with a waterjet, at TechShop. I learned a lot from it, still am, the bike isn't finished =) I'm already planning version 2. However, I am working on funds to order the ultracaps and build flywheel.

My goals: light, inexpensive, cool looking, mcu controlled

$400 later...Let me know what you think!

Update: It is now 36v

Update: Just met with two Maxwell engineers, no sponsorship for ultracaps, but said the 16v module will get my bike up to 20 mph in 10 seconds (back of napkin calc). I will test this calc.
 

Attachments

  • 1.jpg
    1.jpg
    120.4 KB · Views: 1,738
  • 4.jpg
    4.jpg
    146.6 KB · Views: 1,738
Experimental bikes don't need foot pegs! jk I took the pedals off the cranks and bolt them to the front of the lower control arms (rear). It was cheaper and quicker than finding a freewheel for chain ring.
 
Couple pics...I just did the super light weight 36v upgrade, couple 3/4" alum straps. Its solid, definitely temporary...but bodes well on the value add $$. I am working on getting some type of li packs (and caps).
 

Attachments

  • P1000635.JPG
    P1000635.JPG
    190.5 KB · Views: 1,657
  • P1000655.JPG
    P1000655.JPG
    145.7 KB · Views: 1,657
28mph today with 36v, that was cool and scary at the same time. I think I'll turn the amps down :D
 
You keep that thing locked up outside?

Is it waterproof?

What would happen if a kid tries to play with it, is it dangerous?
 
lostrack said:
You keep that thing locked up outside?

Is it waterproof?

What would happen if a kid tries to play with it, is it dangerous?

I am only using it to do research right now (mule vehicle), she's not ready for skin yet. Its definitely not a bike you want to leave outside or unattended in the public, for sure. From what I've learned so far, I would like to build version two, using a real bike frame and brushless motor, but that is down the road.

As far as kids playing with it, they should get zapped (j/k, but how else do they learn?). My normal 2-wheeled vehicle is a 1000cc motorcycle, adults play with that bike when its parked, super annoying that people touch other peoples property; therefore, I have an alarm.
 
From yesterday, got her all back together (it was completely stripped Saturday for tig welding, then that didn't work out). I also took her to 36v (running both 24v motor/controller hehe). Another problem was running #41 chain on the large chainring, the chain had too much axial movement, so luckly I had a parts bike with the same 48t chain ring, I then welded that to the other chainring to make a double-wide chainring. I had to hammer each tooth inward (together), to replicate the fillets on real gear teeth, so far it works much better (I also straightened the rings, that helps too).
 

Attachments

  • P1000669.jpg
    P1000669.jpg
    146.4 KB · Views: 1,591
  • P1000662.jpg
    P1000662.jpg
    138.8 KB · Views: 1,591
Move this to non-hub forum? I figured I'd get some more replies...maybe because I only have 2 posts, therefore I am not real.
 
Hello, very nice build !
could you give more details about your arduino ?
About Ultracaps: what is your goal ?
Thank's
 
jpgey said:
Hello, very nice build !
could you give more details about your arduino ?
About Ultracaps: what is your goal ?
Thank's

I am moving forward with Maxwell to get the capacitor specifications. I need to capture some data (amp / time) then we'll see what it looks like. I am also designing the upgrade to a brushless outrunner.

Arduino Inputs and Outputs can be found in the code below.

Here is the code (self-taught, still learning):
Code:
// Alpha Motor Control program written by Steelmesh
// IR2110 Driver
// 75321 N-mosfet
// ACS756 hall effect current sensor, two 0.1uf caps for VIN/VOUT
// Hall Sensor pins: (1)VCC- 5v Arduino, (2)GND- Ground Arduino, (3)VIOUT- Analog Input Arduino, (4) from power source, (5) to load

int throttleCmd = 3; //Low PWM to IR2110 LIN; digital
int hallIn = 2; //signal from Hall Sensor
int cutOff = 0; //manual limit input pulse amount 0-255
int cruiseControl = 7; //Cruise control
int throttleSignal = 4; //Throttle input; analog
int brakeSwitch = 12; //Safety, pulse and cruise kill
int brakeLight = 10; 
int brakeBlink = 0;
int photoSensor = 8;
boolean toggle = false; //toggle for cruise control
boolean toggleCruise = false; //toggling resume
boolean ampReducer = false; //toggle for ampDelta change reading
int limitRamp = 0; //Starting pulse adder is 0, if over limit, this value increase (pulse - this higher value = lowering pulse)
int ampSurge = 0; //Counter for amp surge sequence
int cruiseCount = 0;
int resumePulse = 0;
int toggleDebug = 0;
int ampDelta = 0;
int buzzer = 11;
int buzzerCount = 0;

//*******MANUAL INPUTS BEGIN*******
int ampLimit = 25; //manual input actual amps, see also limitRamp above
int sensorCal = 531; //531 manual input raw input at zero amps for Hall Sensor
int sensorSpec = 40; //Hall Sensor, 40 mV per Amp

int cruiseLimit = 20; //manual input for amps, cruise economy
int cruiseDebouncePre = 10; //Actual ms to re-read button
int cruiseDebouncePost = 300; //Actual ms to wait for operator to release button
int cruiseCycles = 300; //number of processing loops before it toggles resume off

int ampSurgeMax = 5; //A loop-counter value to allow the amp surge mode, it will bypass limitRamp for "ampSurgeMax loop cycles"
int ampSurgeThreshold = 240; //pulse value for minimum throttle pulse to kick in ampSurge, see ampSurge above
int ampSmoothing = 2; //Actual amps to kick in ampDelta smoothing
int ampReducerCount = 0;

int duskDawnSet = 300; //photoSensor actual value
//*******MANUAL INPUTS END*******

void setup()
{
  Serial.begin(9600);
  pinMode(throttleCmd, OUTPUT);
  pinMode(brakeSwitch, INPUT);
  pinMode(throttleSignal, INPUT);
  pinMode(cruiseControl, INPUT);
  pinMode(buzzer, OUTPUT);
  pinMode(brakeLight, OUTPUT);
}

void loop()
{
  if(buzzerCount < 1) //initial buzzer
  {
    digitalWrite(buzzer, HIGH);
    delay(130);
    digitalWrite(buzzer, LOW);
    buzzerCount = 1;
  }
  
  int throttleIn = analogRead(throttleSignal); //Main read throttle, declare pulse
  int pulse = constrain(throttleIn, 0, 1023);
  pulse = map(pulse, 0, 1022, 0, 254);
  //pulse smoothing

  
  int cruisePulse = pulse;
  
  if (pulse < 55)
  {
    limitRamp = 0; //Reset rampLimit to zero at zero throttle
  }
  
  if (pulse < ampSurgeThreshold)
  {
    ampSurge = 0; //Reset ampSurge counter if throttle falls below threshold
  }
  
  int ampRead = analogRead(hallIn); //Read hall sensor output
  ampRead = constrain(ampRead, sensorCal, 1023); //constrain to manual sensor calibration
  int actualAmp = ampRead - sensorCal; //Declare new int for conversion
  actualAmp = (actualAmp * 4.9) / sensorSpec; //4.9 * Analog signal =~ mV, divide by example: "ACS756 spec: 40mV/1A"
  int hallMv = analogRead(hallIn) * 4.9; //debugging to view mV
  

  // ***************CRUISE CONTROL BEGIN*****************
  
  //toggle Cruise
  if (digitalRead(cruiseControl) == HIGH && toggle == false)
  {
    delay(cruiseDebouncePre);
    if (digitalRead(cruiseControl) == HIGH && toggle == false) //debounce
    {
    toggle = !toggle;
      if (toggleCruise == false && pulse > 80) 
      {
        throttleIn = analogRead(throttleSignal);  //Grab pulse value immediately
        pulse = constrain(throttleIn, 0, 1023);
        pulse = map(pulse, 0, 1022, 0, 254);
        limitRamp = 0;
      }
      if (toggleCruise == true && pulse > 80) 
      {
        throttleIn = analogRead(throttleSignal);  //Grab pulse value immediately
        pulse = constrain(throttleIn, 0, 1023);
        pulse = map(pulse, 0, 1022, 0, 254);
        limitRamp = 0;
      }
      if (toggleCruise == true && pulse < 80)
      {
        pulse = resumePulse; //If shut down was via braking, this saves cruise...resumePulse can only initially be set by entering cruise loop first with toggleCruise false
        pulse = constrain(pulse, 0, 254);
        Serial.println("Resume Saved");
      }
    delay(cruiseDebouncePost);
    Serial.println("Cruise On");
    }
  }
  //toggle Cruise end

  //Cruise loop begin
  while (toggle == true)
  {
    Serial.print("Cruise Running at ");
    Serial.print(pulse);
    Serial.print(" pulse");
    Serial.print(" ");
    Serial.print("Amps: ");
    Serial.print(actualAmp);
   
    Serial.println("");

  
    ampRead = analogRead(hallIn);
    ampRead = constrain(ampRead, sensorCal, 1023);
    actualAmp = ampRead - sensorCal;
    actualAmp = (actualAmp * 4.9) / sensorSpec;
   
   int cruiseOnSmoothing = cruiseLimit; //smooth out amp surge when cruise resuming
  cruiseOnSmoothing = cruiseOnSmoothing * 1.5;
  if (actualAmp > cruiseOnSmoothing)
  {
    limitRamp = limitRamp + 30;
    limitRamp = constrain(limitRamp, 0, 254);
  }
  
   //current limiter function under cruise
  if (actualAmp > cruiseLimit)
  {
    limitRamp = limitRamp + 2;
    limitRamp = constrain(limitRamp, 0, 254);
  }
  
  if (actualAmp < cruiseLimit)
  {
    limitRamp = limitRamp - 2;
    limitRamp = constrain(limitRamp, 0, 254);
  }

  cruisePulse = pulse - limitRamp;
  cruisePulse = constrain(cruisePulse, 0, 254);
  analogWrite(throttleCmd, cruisePulse);

    
    int brakeRead = digitalRead(brakeSwitch);
      if (brakeRead == LOW)
         {
         toggle = !toggle;
         toggleCruise = false; //reset resume pulse number by toggling it off for next command
         analogWrite(throttleCmd, 0);
         pulse = 0;
         Serial.println("Cruise OFF brake");
         delay(cruiseDebouncePost);
         } 
    int cruiseRead = digitalRead(cruiseControl);
      if (cruiseRead == HIGH && toggle == true)
         {
         delay(cruiseDebouncePre);
         if (cruiseRead == HIGH && toggle == true)
           {
           toggle = !toggle;
           resumePulse = pulse; //if user uses the brake switch to cut off cruise, it will save this value as resumePulse for cruiseCycles cycles
           toggleCruise = true; //toggle on cruise resume pulse
           cruiseCount = 0; //reset timer (timer to deactivate resume)
           analogWrite(throttleCmd, 0);
           Serial.println("Cruise OFF button");
           Serial.println("Cruise Pulse Saved");
           Serial.println(resumePulse);
           delay(cruiseDebouncePost);
           }
       }
   }
   //********************CRUISE CONTROL END**************************
   
  
    while (digitalRead(brakeSwitch) == LOW)
  {
    analogWrite(throttleCmd, cutOff);
    digitalWrite(brakeLight, HIGH);
    delay(200);
    Serial.println("Brake Shut Down");
  }

  
int lightSwitch = analogRead(photoSensor);
lightSwitch = constrain(lightSwitch, 0, 800);

   if (brakeBlink <= 2 && lightSwitch <= duskDawnSet)
    {
    digitalWrite(brakeLight, HIGH);
    brakeBlink = brakeBlink + 1;
    }
   if (brakeBlink > 2 && brakeBlink <= 15 && lightSwitch <= duskDawnSet)
    {
    digitalWrite(brakeLight, LOW);
    brakeBlink = brakeBlink + 1;
    }
   if (brakeBlink >= 16)
    {
    brakeBlink = 0;
    }
   if (lightSwitch > duskDawnSet)
   {
     digitalWrite(brakeLight, LOW);
   }
  
  
  Serial.print("Pulse: ");
  Serial.print(pulse);
  Serial.print(" ");
  Serial.print("Amps: ");
  Serial.print(actualAmp);
  Serial.print(" ");
  Serial.print("Hall mv: ");
  Serial.print(hallMv);
  Serial.print(" ");
//  Serial.print("ActualV: ");
//  Serial.print(actualVolt);
//  Serial.print(" ");
  Serial.print("Surge Count: ");
  Serial.print(ampSurge);
  Serial.print(" ");
  Serial.print("BrakeSw: ");
  Serial.print(digitalRead(brakeSwitch));
  Serial.print(" ");
  Serial.print("Resume: ");
  Serial.print(resumePulse);
  
  if (toggleCruise == false)
  {
   toggleDebug = 0;
  }
  else
{
  toggleDebug = 1;
}
  Serial.print(" ");
  Serial.print("Cruise: ");
  Serial.print(toggleDebug);
  Serial.println("");
 
  
  if (actualAmp > ampLimit) //Main current limiter function
  {
    limitRamp = limitRamp + 2;
    limitRamp = constrain(limitRamp, 0, 254);
    Serial.println("OVERLIMIT");
  }
  else
  {
    limitRamp = limitRamp - 2;
    limitRamp = constrain(limitRamp, 0, 254);
  }


//if (ampReducer == false && ampReducerCount < 1) //grab amp reading into ampDelta 
//{
//  ampDelta = actualAmp;
//  ampDelta = ampDelta + ampSmoothing;
//  ampReducer = true;
//  Serial.println("AmpReadGrab");
//}
//
//ampReducerCount = ampReducerCount + 1;
//
//if (ampReducer == true && ampReducerCount > 3) //wait for ampReducerCount counter to reset toggle
//{
//  ampReducer = false;
//  ampReducerCount = 0;
//}


  if (ampSurge < ampSurgeMax && pulse > ampSurgeThreshold) //AMP SURGE MODE, Full Throttle, gives "ampSurgeMax cycles" of full pulse commands bypassing limitRamp
  {
    analogWrite(throttleCmd, pulse);
    ampSurge = ampSurge + 1;
    limitRamp = 0; //Prevent adding to limitRamp while in ampSurge mode, so when exiting ampSurge, limitRamp starts changing
    Serial.println("SURGE MODE");
  }
//  else if (ampDelta < actualAmp)
//  {
//     pulse = pulse - 30;
//     pulse = constrain(pulse, 0, 254);
//     analogWrite(throttleCmd, pulse);
//     Serial.println("Amp Reducer");
//  }
  else
  {
    pulse = pulse - limitRamp; //Current ramping equation, if not in ampSurge mode
    pulse = constrain(pulse, 0, 254);
    analogWrite(throttleCmd, pulse); //Main Throttle Command Line
  }

  int lastPulse = pulse; //grabs what the last pulse was for pulse smoothing
  //delay(50); //Serial.print sake
}
 
Back
Top