I first started longboarding about half a year ago, and ever since have wanted to be able to get over hills and put minimal effort into cruising around. Without the budget (or desire) to buy an electric longboard, I set out to build my own.
Files
Feature List and preliminary pseudocode.pdf
Adobe Portable Document Format -
310.00 kB -
04/28/2016 at 04:51
I have taken the pseudocode and created version 1.0 of the code. Since last time, I have decided to take a slightly different path and further complicate the electronics and software side of things. Since I want the connection for commands from my phone to be constantly polled, I was struggling with combining all parts of the code into one file to run on one board. As such, I have moved to a two-board idea where a 5V 16Mhz Adafruit Trinket controls the motor, reads sensor data, and has most of the features the pseudocode mentioned, and the 3.3V Adafruit Feather Bluefruit M0 handles polling for commands and converting these into meaningful signals for the Trinket to read.
In the future, it may be possible to combine all parts of the code into the Feather, but for now this is the approach I am taking. The Trinket will have 3 analog inputs, 2 for the force sensors and 1 for the speed command from the Feather. It will have one digital input from the Feather as a killswitch pin to cause the Trinket to initiate the deceleration loop if it goes high. It will have one digital output as the PWM output to the ESC.
The Feather will have an analog output to the Trinket for speed control, and an analog input from the ESC for the current speed (just realized this may be a 5V signal while the Feather is 3.3V logic). It will be commanded by the Bluefruit companion app with 3 commands; 1) increase speed 2) decrease speed and 3) stop the longboard.
My current concerns involve the difference in logic voltage levels between the two boards, the increasingly complex nature of the whole electronics side of things, and controlling the ESC with just a simple PWM.
/*********************************************
* Note the numbering of analog pins: Pin 2 is Analog 1, Pin 3 is Analog 3, Pin 4 is Analog 2.
* For the Uno, the terms A1, A2, and A3 are mapped for you. For ATtiny85's, they are not.
* So for the pinMode calls, use the Pin number (stenciled on Trinket), for analogRead, use the analog number.
*
*/
#include <Servo.h>
int desiredSpeedFrmCtrl; //analog input connected to analog pin #2
int force1; //first force sensor (analog) connected to analog input pin #4
int force2; //second force sensor (analog) connected to analog input pin #3
int forceThres; //threshold for force sensors to cutoff and read an error
int forceVal1;
int forceVal2;
int stopPin = 0;
int stopSpeed; //speed variable for the deceleration loop to use
Servo ESC; //ESC object to be able to write PWM to
void setup() {
// put your setup code here, to run once
ESC.attach(0);
pinMode(2, INPUT); //analog input
pinMode(3, INPUT); //analog input
pinMode(4, INPUT); //analog input from auxillary board for BLE commands
pinMode(0, INPUT);
forceThres = 400; //arbitrary threshold until the sensors are calibrated
Serial.begin(115200);
}
void loop() {
// put your main code here, to run repeatedly
startupLoop();
runLoop();
}
void runLoop() {
//code for the main running loop of the board
forceVal1 = analogRead(2);
forceVal2 = analogRead(3);
while((forceVal1 > forceThres) || (forceVal2 > forceThres)){
desiredSpeedFrmCtrl = analogRead(1);
ESC.write(desiredSpeedFrmCtrl);
Serial.print("Speed sent to ESC: "); //for debugging purposes (speed read from feather and sent to ESC)
Serial.print(desiredSpeedFrmCtrl);
Serial.println();
forceVal1 = analogRead(2);
forceVal2 = analogRead(3);
if(digitalRead(0) == HIGH){ //if there was a command from the controller to stop the decel loop is called
decelerationLoop();
return; //the runLoop breaks becak into the main loop
}
}
}
void startupLoop() {
//code to determine if the board can startup
while (1 > 0){
forceVal1 = analogRead(2); //read the analog value from the first force sensor
forceVal2 = analogRead(3); //read the analog value from the second force sensor
if((forceVal1 > forceThres) && (forceVal2 > forceThres)){
ESC.write(0); //set speed to 0
return; //exit to main loop
}
delay(200);
}
}
void decelerationLoop...
I got around to start writing a basic feature list and some pseudocode for the longabord, which will all be written in Arduino running on the Adafruit feather M0 with Bluefruit. I have uploaded the document to the files section but also want to go over the code here.
First off a basic feature list:
Failsafe mechanisms (Connectivity loss, rider falls off, user indicates an error, low voltage)
Smooth acceleration/deceleration ability
Error state to enter and stop the board in case of one of the failsafes triggering
LED control
Now for some pseudocode to accomplish these goals:
Initialization of variables
Variables for speed/throttle
Variables for LED color
Declare ESC Servo object
Variables for sensors
There will be variables for the speed and throttle, at least two. These two will be the current speed, obtained from the RPM sense wire coming from the ESC, and the desired speed or throttle, coming from commands from the bluetooth connection to the phone app. LEDs will be declared, and the ESC will exist as a Servo object as it needs to be sent PWM for throttle control. There will also be other sensors such as 2 piezoresistive force sensors, and potentially voltage sensors, which would be analog inputs.
void setup ()
Attach ESC to correct pin
Attach LEDs to correct pins
Start Serial for debugging
The ESC Servo object will be attached to a pin as well as the LEDs to their respective pins. The Serial interface will also be started to send information to either the computer or phone if debugging is needed.
void loop () (main
loop)
Call startup function if all conditions are met
Set speed to 0
Call run loop
run loop loops within itself, only exits if base/error conditions are met
Here are the first instances of "startup function" and "run loop," which are just names at this point but will be described later. The main loop of the code will basically exist to call other loops, but it is important because if the run loop throws an error, calls the "deceleration loop," and breaks, we want it to run the startup function again to start everything over.
run loop
Run forever
Read all sensor data
Read all throttle/speed data
Read throttle command from app, reassign to desired speed variable every loop
Calculate difference between current speed and desired speed
Write first step above/below current speed (fixed step size)
Delay fixed time period (probably 50-200ms)
If any bases cases are met (sensors, connection, user command)
Call deceleration error loop
break (Will break to main loop, which will loop around to startup function)
The run loop is where most of the time will be spent while riding. It is called after the startup function, and loops while everything is "OK," only changing throttle when prompted by the user. It will first read the sensor data from the force sensors, then get the current speed from the RPM sense wire, and also read the current speed command from the app. This "desired speed" should be reassigned to the same variable every time the loop runs to ensure the user can change speed before the board actually gets to that speed. It should then calculate the difference between the current speed and the desired speed, and then move in the correct direction by telling the ESC to run at a speed a fixed step size higher or lower than the current speed. This happens only once per loop so the board can accelerate or decelerate at a comfortable pace, but also continuously check to see if anything has gone wrong. The delay is built in so the loop only runs ideally 5-20 times per second, instead of 1000s of times per second so the board doesn't jump to the desired speed and leave the rider in the dust (The motor can go 0-full speed (albeit, unloaded) in 300us, according to the spec sheet). The run loop then checks the numerous "failsafe" cases, if both force sensors read below a threshold (rider has fallen off), if connection is lost, and if the user wants to board to slow down immediately. If any of these conditions are true, the loop will...
Read more »
All the parts I ordered from Adafruit, HobbyKing, and SDP/SI have arrived.
From Adafruit I got:
2 voltage sensors with LCD panels
5V to 3.3V regulator
Adafruit Feather M0 Bluefruit
From HobbyKing I got:
HobbyWing 100A Platinum ESC
Turnigy AeroDrive SK3 375kV brushless motor
2 4000mAh 3S LiPo batteries
Fireproof LiPo Bag
LCD ESC programming module
1-6S LiPo Charger
And from SDP/SI I got:
2 Plastic 24T timing pulleys
1 Aluminum 10T timing pulley
2 265mm length timing belt
All the parts that end up in the final design will be put in the components section and more detail will be provided about each of them.
I didn't know it but the ESC also gives and RPM sensor lead, which is super useful as I needed a way to check speed in the code. See the next log and the feature list and pseudocode document in the files section.
I have encountered the first setback of the project.
I was riding the board I was planning on converting, fell off, it went into the road, and a car ran over it. Trucks and wheels are fine, but a new deck is necessary.