I received a request on YouTube to share my code. I figured this would eventually happen as when I was looking for functioning code myself I kept finding videos of people who succeeded, but didn't share how they did it. My video above is pretty much the equivalent of those videos. Success, but no information as too how. I was hoping to have this completed and the code posted here before the first request came. But I'll post what I have so far to assist anyone that finds themselves needing the information.
While I'm at it, I'll post an update as too where I'm at with this project.
This project is more for fun, and too see if I can actually accomplish this. I've seen people use these gauges to build functioning instrument panels for vintage museum aircrafts, video game driving/flying simulators, RC aircraft's to display RPM, Velocity, and pitch. In each of those cases the gauge is installed in a panel that is not subject to vibrations. In my case the gauge will be stressed by the vibrations of an Old vintage motorcycle. If I'm successful with this gauge rebuild I do plan to run it on the motorcycle out of curiosity. I don't plan on it lasting very long but I will be pleasantly surprised if it does.
Here are some pictures of where I'm currently at..
soldering the wires to the motor
running the wires through the 3d printed mounts
The Hall effect holder, with internal channel for wire routing
test fitting the pieces outside the tach frame
The tachometer frame with the bar magnet shield in place.
Cutting off the shield to allow room for the 3d printed parts
All the parts mounted together
Test fitting in the speedometer housing
Materials you will need:
1 - Arduino
1 - X25 or X27 stepper motor
1 - 10k resistor (to be placed on arduino between pin-2 and vin to keep pin from bouncing)
1 - A1344 hall effect sensor
various lengths of wires and a method to mount the devices for your application.
(NOTE: WHEN USING GUY CARPENTERS LIBRARY NO H-BRIDGE DRIVER IS NEEDED. THE MOTOR IS DRIVEN DIRECTLY
FROM THE ARDUINO)
I will also be adding a L805CV voltage regulator to protect the board from electrical power spikes created by the motorcycle.
(The CB450 runs on 12V of power, and the arduino has been approved for 12V applications. However, I worry that the small
voltage regulator on the arduino board will not be strong enough to withstand a voltage spike from a motorcycle. The L805CV
can withstand a voltage spike of 35Volts and 2Amps and if much more likely to survive such a scenario while at the same time
lowering the voltage to 5Volts)
As for the Code:
The wiring for the X25.168 or X27.168 stepper motor
Guy Carpenters X25/X27 motor library that needs to be loaded onto your Arduino IDU to control the motor
View attachment SwitecX25-master.zip
A link explaining how to load a .zip library to your Arduino IDU
https://www.arduino.cc/en/guide/libraries
"https://www.arduino.cc/en/guide/libraries"
The CODE, with TONES of notes to help anyone that finds this useful. Please note that I am currently still working on the final pieces of the code to give the correct angle for each reading of RPM's. A final simple piece. But since this code was requested I'm sharing it here in it currently unfinished, but functioning, form.
************************************************************************************
*************COPY EVERYTHING BETWEEN THE ASTERISK'S INTO YOUR ARDUINO IDU*************
************************************************************************************
//----------------------------------------------------------------------
//
https://github.com/clearwater/SwitecX25
//
// This is an example of using the SwitchX25 library.
//
// This code has been rewriten from Guy Carpenters X25 example to
// function as a Tachometer for a CB450 K0 Motorcycle (Tach
// gearing ratio of 1:7)
//
// Note that the maximum speed of the motor will be determined
// by how frequently you call update(). If you put a big slow
// serial.println() call in the loop below, the motor will move
// very slowly!
//----------------------------------------------------------------------
// Guy Carpenters X25 library
#include <SwitecX25.h>
// standard X25.168 & X27.168 range 315 degrees at 1/3 degree steps
#define STEPS (315*3) //totaling 945 steps to complete 315 degrees of rotation
// For motors connected to digital pins 4,5,6,7 (on X25 & X27 pin 1-> pin4, pin 2-> pin5, pin3 -> pin6, pin4 -> pin7)
SwitecX25 motor1(STEPS,4,5,6,7);
// digital pin 2 is the hall effect sensor input pin (AT ARDUINO, ADD A 10K RESISTOR BETWEEN PIN 2 AND VIN TO KEEP PIN FROM 'BOUNCING'!!!!)
int hall_pin = 2;
// pick number of rotations to analyze to determin rpm (higher improves accuracy)
float rotations = 1.0;
// Time program has spent in "While" loop per half rotation of tach cable
float WhileTime2;
// each rotation trips hall effect 2 times due to each half of bar magnet triping hall effect sensor once
// correct number of trips per rotation.
float hall_thresh = (rotations*2);
// tach cables rpm result
float rpm_val;
// to mark the first turn of tach cable. due to no previous recording for position of magnet.
int first_run;
// used to record previous state of hall effect sensor
int previous_hall_pin_state;
void setup(void)
{
// run the motor against the stops at start up
motor1.zero();
// start moving towards the 0 value (for my wiring and tach gauge 0 value is at positon 945)
// changing the wiring may change the 0 position to postion 0. If wiring is changed code will need to be updated.
motor1.setPosition(944);
// Mark that first turn of tach cable has NOT happened yet
first_run = 0;
// make the hall pin an input:
pinMode(hall_pin, INPUT);
// initialize serial communication at 9600 bits per second:
// Serial.begin(9600);
}
void loop(void)
{
// nextPos will be the value where the needle will be moved too
// setting nextPos to 0 at begining of loop does not move the motor, it only clears the value to
// prepare it for its new value
static int nextPos = 0;
// the motor only moves when you call update. It will pull the value from the last motor1.setPosition()
// value in your code
motor1.update();
// preallocate values for tach (clear values at begining of new loop for new data and calculations)
// rotations equal 0
float hall_count = 0.0;
// start timer
float start = micros();
// while loop timer
WhileTime2 = 0;
// Marks current status of hall effect sensor (0=ON, 1=OFF)
// NOTE: THIS WAS USED ON THE EARLY DESIGN OF THE CODE. THE CODE HAS CHANGED SINCE THEN, AND
// I AM UNSURE IF THIS MARKER IS STILL NEEDED. WILL LOOK INTO LATER
int on_state = 0;
// counting number of times the hall sensor is tripped
// While code is in "While loop" code in main loop is paused. be sure to include "motorl.update()"
// In "While" loop to keep motor running smoothly
// run "While" loop as long as number of sample rotaions entered in "roataions" above have not been meet
while(hall_count < hall_thresh){ //remember hall_count = 2*rotations
// continue to update motor position even when code is stuck in while loop
motor1.update();
// The amount of time needed for 100rpm reading (used to send needle to 0rpm when readings under 100rpm are received
// or no data is received and time threshold for 100rpm has been exceded for Honda CB450)
// Value is in Micro-Seconds
float hund_rpm_time = ((30000000/10)*hall_thresh);
// The amount of time program has spent in "While" loop for each loop in the "While" loop
WhileTime2 = micros()-start;
// If time in 1 pass through a "While" loop exceeds time for 100rpm minimum
// then begin to send needle to 0rpm position
// NOTE: INCREASE THE "14" FOR FASTER NEEDLE RESPONSE. I'LL LOOK INTO THIS PART OF THE CODE FURTHER IN THE
// FUTURE TO SEE IF THIS EFFECTS ACCURACY
if (WhileTime2 > (hund_rpm_time/14)){
//Sends motor to position 944 (0 rpms for this code)
motor1.setPosition(944);
}
// if magnet is triggering hall effect sensor then...
if (digitalRead(hall_pin)==0){ // hall effect Sending signal (hall_pin = 0)
// if first_run = 0 then the Tach cable has not turned yet. Thus, during previous drive Tach magnet came to rest next
// to hall effect sensor once engine was turned off. reading is false.
if (first_run = 0){
// mark hall effect sensor as previously on (previous_hall_pin_state = 0)
previous_hall_pin_state = 0;
// mark first_run = 1 so this section of code only runs at the very first turn of tach cable only
first_run = 1;
// incriment tach cable rotation value by 1/2 (remember that 2 counts are equal to 1 rotation of the cable)
// TECHNICALLY THE CABLE HAS NOT TURNED AT ALL. INCIMENTING MAY NOT BE NEEDED HERE. HOWEVER, IF INCIMENTING
// HERE IS PRODUCING A FALSE VALUE IT IS ONLY FOR THE 1ST VALUE AND THEN IGNORED. ARITHMETIC ERROR IS UN-NOTICABLE
hall_count+=1.0;
}
/// if hall effect sensor is on now and was previoulsy off then...
if (digitalRead(hall_pin)==0 && previous_hall_pin_state==1){
/// if on_state is equal to 0 (ON) then set on_state equal to 1 (OFF) to prepare for next pass,
// increase hall_count, change previous_hall_pin_state=0 (ON)
if (on_state==0){
on_state = 1;
hall_count+=1.0;
previous_hall_pin_state = 0;
}
}
// if hall_pin = 1, magnet is NOT triggering hall effect sensor then...
} else{
// if you reach this point in code and still first_run = 0
// then Tach cable has not turned yet. And you know that the end of the Tach bar magnet
// did NOT come to rest next to hall effect sensor when engine was last turned off
// mark hall effect sensor as previously off (previous_hall_pin_state = 1)
if (first_run = 0){
previous_hall_pin_state = 1;
// mark first_run = 1 so this section of code only runs at the very first turn of tach cable only
first_run = 1;
}
// if hall effect sensor is off now and was previoulsy on then...
if (digitalRead(hall_pin)==1 && previous_hall_pin_state==0){
//set on_state = 0 (set marker to ON) to prepare for next time hall_pin=0 (ON)
on_state = 0;
// change previous_hall_pin_state=1 (OFF)
previous_hall_pin_state = 1;
}
}
// if number of data gathering cable rotations have been met then exit "while" loop
// and begin doing RPM calculations
if (hall_count>hall_thresh){
break;
}
//continue to update motor position even when code is stuck in while loop
motor1.update();
}
// record current time to compare to start time
float end_time = micros();
// calculate how much time has passed in seconds (dividing microseconds by 1 million gives you seconds)
float time_passed = ((end_time-start)/1000000.0);
// calculate rpm value (hall sensor is tripped twice per rotation, so divide hall_count in half to get
// number of rotations. then divide by the amount of time that passed to get rotations per second.
// multiply by 60 to get rotations per minute
rpm_val = ((hall_count/2)/time_passed)*60.0;
// send value to motor to rotate needle.
// NOTE: CB450 TACH GEARING IS 1:7. SO RPM'S WILL BE (1/7)OF THE MOTORS ACTUAL RPM's
// (for values greater than 1571) situation where tachometer has reached its max amount (Motorcycle about to EXPLODE!)
if(rpm_val > 1571){ //CB450 K0 tach max at 11000rpm (tach ratio of 1:7. 11000/7=1571)
nextPos = 472; // turn gage about 180 deg (X27 turns 945 steps. 945/2=472). (ACTUAL ANGLE FOR CB450 IS SLIGHLTY LESS. WILL WORK OUT LATER)
motor1.setPosition(nextPos); // call needle to rotate to given potition
nextPos = 0; // clear nextPos value to zero for next calculation
//send value to motor to rotate needle (for values between 0 and 1571, between 0 and 11000 rpm)
}else if(rpm_val >= 0 && rpm_val <= 1571){
// nextPos represents "motor potition" or steps. Since 944 is our zero we will work backwards by subtracting he the steps from our zero mark (944).
// first we need to covert our RPM's to steps. Since we are only using 180 degrees of the motor for the CB450 K0 Tach our max steps is 472 (half of 945).
// these 180 degrees will represent a max of 11000 rpms. Since our gearing ratio is 1/7th of the motor that is a max of 11000/7= 1571 rpms of the cable in these 180 degrees.
// so our conversion factor is (472/1571). we multiply our RPM's (rpm_val) by (472/1571) to get the number of steps to subtract from our zero point.
// ..................its not as confusing as I'm explaining. I'm just doing a horrible job.
nextPos = 944-((rpm_val*472)/1571);
motor1.setPosition(nextPos); // call needle to rotate to given potition
nextPos = 0; // clear nextPos value to zero for next calculation
}
}
************************************************************************************
************************************************************************************
-Isaias