MagnetoSpeed mod ghetto

Här kan du skapa ett raportage om ditt pågående projekt. Det kan vara en ny stock eller kanske ombyggnation av ett helt vapen. Alla trådar skall dock innehålla bilder på ditt projekt.

Moderatorer: taxen, Kari Lindström, potatiskastare, Trond Åsly, mecke, Stefan.K, Ulrik, Fritz Kraut, PerN, Robban

Kategoriregler
Trådar som skapas här skall vara ett pågående projekt som man själv bedriver. Dvs. Inga trådar som innehåller en fråga osv. En projekttråd skall även innehålla en bild eller gärna fler. Trådar som inte uppfylller dessa enkla regler kommer raderas utan förvarning!.

Re: MagnetoSpeed mod ghetto

Inläggav sevehem » 16 okt 2019, 20:34

Åker också på bönpallen, klädsamt tyst i min okunskap. Älskar dessa trådar.
Söker kombipipset till Antonio Zoli Delfino, kaliber är av sekundär betydelse men klass1 är ett plus
Användarvisningsbild
sevehem
 
Inlägg: 2238
Blev medlem: 14 jun 2013, 21:20
Ort: Varberg

Re: MagnetoSpeed mod ghetto

Inläggav Bjerker » 16 okt 2019, 21:16

Mycket intressant!

Skickat från min SM-G970F via Tapatalk
Bjerker
 
Inlägg: 134
Blev medlem: 08 sep 2011, 08:34

Re: MagnetoSpeed mod ghetto

Inläggav marlin1895g » 16 okt 2019, 22:55

Intressantaste tråden på forumet just nu enligt mig


Skickat från min iPhone med Tapatalk
Användarvisningsbild
marlin1895g
 
Inlägg: 890
Blev medlem: 15 jan 2007, 21:56
Ort: Huddinge

Re: MagnetoSpeed mod ghetto

Inläggav Fält » 17 okt 2019, 10:03

Som ett första test så är det enormt bra resultat, det kan ju gå hur dåligt som helst när man skall prova något i praktiken första gången.

GGG anger 1015m/s i 600mm pipa. Tyvärr så har jag inte mätt hastigheten på några fabriksladdade GGG 55gr patroner i min 20" pipa.
Fält
 
Inlägg: 1128
Blev medlem: 21 jan 2016, 15:58

Re: MagnetoSpeed mod ghetto

Inläggav lasse-a » 17 okt 2019, 12:11

Tack alla som fortfarande hänger med! :)

Då har vi iaf en datapunkt vad gäller rimligheten. Det jag naturligtvis är rädd *) för är om jag skulle trigga (pga brus) på den positiva flanken på en av signalerna. Med mätningarna från förra gången så skulle det (i runda slängar) kunna ge 5% fel. Dvs 50 m/s här.

Men eftersom jag inte har den ES på någon av serierna så är den risken förhoppningsvis liten. Dvs jag triggar på rätt flank på båda sensorsignalerna. Jag får bygga mig en splitter och släpa med mig oscilloskopet och göra parallella mätningar nästa gång. Kanske även min gamla ShootingChrony, skulle vara kul att få värden från samtliga system. I bänk så har det som sagt alltid fungerat. Även med "riktiga" signaler, dvs som jag samplat från skjutning.


Men att få sådana här dåliga värden för SD och ES är alltså inget att höja på ögonbrynen för given bössan och ammon? Jag har ju bara mätt (mina egna) handladdningar tidigare, och såg det ut såhär så är ju något inte som det skall.


* Eller "rädd" ... Jag har ju gevär så jag är ju inte rädd för en kronograf. ;) Möjligtvis rädd om... ;) Nej, även om jag inte hade visir den här gången så har jag iaf glasögon. Det räcker ju att kulan tar i en av magneterna för att det skall kunna bli riktigt otrevligt splitter. Nu är ju risken hanterbar, men ändå.
Användarvisningsbild
lasse-a
 
Inlägg: 1004
Blev medlem: 21 jun 2018, 08:19
Ort: Västergötland

Re: MagnetoSpeed mod ghetto

Inläggav lasse-a » 22 okt 2019, 09:28

Då var koden skriven och "avlusad". Nåja, det döljer sig fortfarande buggar (säkert) men det funkar iaf när jag provar.

Jag bygger apparaten efter följande: Man kan ha 1-99 olika strängar med skott, och 0-99 skott i varje sträng. Skotten lagras i EEPROM, alltså permanent minne, så om man stänger av apparaten så ligger de kvar tills man tar bort dem. Jag lagrar räknarvärdet och konverterar till hastighet vid display. Så ingen precision går förlorad (det går att läsa ut alla värden via USB-porten). Eftersom den Arduino jag använder har 512 bytes EEPROM-minne, och varje skott tar tre bytes (ett för skottsträng, och två för räknarvärdet) så kan man lagra totalt 170 skott innan minnet är fullt. Detta fritt fördelat på de olika strängarna (10 skott på sträng 1 och 100 på sträng 15 är alltså helt OK).

Eftersom jag har många knappar man kan trycka på så blir den lite lättare att köra än sådana här brukar vara.

Det finns två lägen: ett om man håller knappar intryckta när man slår på, och ett när den är i körläge. I startläget så kommer man åt funktioner som; radera alla lagrade data, visa matningsspänningen, visa triggnivåspänningen för komparatorn (båda i mV), lagra testskott i minnet (för att förenkla felsökning) samt skriv ut alla lagrade data som en CSV-fil på USB-porten. Det senare för att man skall kunna importera sina resultat i ett beräkningsprogram (exv. excel).

[code][/code]
När kronografen är i körläge (visar -READY-) så väntar den på skott eller knapptryckning. Med två knappar så kan man öka eller minska aktuell sträng, och två andra så kan man öka eller minska visat skott. Om inget skott finns för den strängen och skottet så visas 0.0 som hastighet. Om det kommer ett skott så visas sträng/skott och hastigheten med fyra siffror. Om hastigheten är < 1000 m/s så visas en decimal, och är den över så blir det ingen decimal utan ett helt tal. Skott lagras alltid sist i aktuell sträng, men om man gjort fel, dvs glömt att byta, så finns det två knappar som ändrar strängen för det sist lagrade skottet. (Har man hunnit skjuta ett till så är man torsk. Då får man leva med det tills man kommit hem.)

En knapp cyklar mellan AV/SD/ES/HI/LO för aktuell sträng, och en sista knapp visar det senast skjutna skottet, eller, om man håller den inne i fyra sekunder, raderar det sist lagrade skottet. (Vill man ta bort ett tidigare så får man antingen radera vidare, alltså näst sista osv, eller stå sitt kast).

Det blir bilder på display senare (sitter dumt till nu), men jag har 8 siffror på mig. Dessa visar aktuell sträng (två siffror), aktuellt skott (två siffror), samt fyra siffror för hastigheten enl. ovan. För AV/SD osv. så visas inte aktuellt skott (det finns ju inget) utan AV/SD osv. visas istället för dessa två.

I vilket fall, för den som vill botanisera vidare så skickar jag med koden. (Det kan är någon mer än Fält som skulle kunna tänkas vara intresserad av den. ;)) Den tar upp nästan 80% av programminnet på min uC, och lämnar 500 bytes (lite drygt) av RAM för exekvering. Så det finns inte plats för jättemycket mer.

I vanliga fall så brukar ju folk som jag hålla på sådan här kod eftersom "Den behöver snyggas upp innan jag släpper den". Läs: "Kommer inte att hända". Och varför är det nu så då? Jo, vi vet precis hur man skall göra, men det är jobbigt och inget skoj, så vi hackar mest på. Och då blir det lite "ståltråd och eltejp" över det hela. Den här koden lider exv. av flera brister; abstraktionerna är hur-som-haver, dvs inte konsistenta och minimala. Behövde jag en, eller trodde att jag skulle komma att behöva, så lade jag till den. Det är också upprepningar, alltså klipp-och-klistra på många ställen. Vilket gör ändringar svårare (men i gengäld så blir det lite lättare att läsa eftersom man inte behöver bläddra fram och tillbaka, överabstraktion är också en risk).

Spelar det här då någon roll? Tja, lite roll spelar det; jag hade en sen bugg (dvs fel) som inte yttrade sig förrän jag körde apparaten i bänk och som väl tog en två timmar att hitta. Den hade gått fortare att hitta, eller rättare, aldrig uppkommit, om jag gjort "rätt" från början. Det hade å andra sidan antagligen tagit två timmar eller mer...

Nu är det bara 500 rader, så dessa problem blir hanterbara, men vackert är det inte! :)

// This is the one that drives the actual hw. lasse-a 2019-10-21
#include <TM1638plus.h>
#include <EEPROM.h>


/*
Drive the hardware that is my MagnetoSpeed chronograph

Functionality:

Measure voltage for a second or two when switched on, display "LOW BATT" if Vcc is low.

99 Shot strings (display limit) (two buttons, up/down)
99 Shots per string (display limit) (two buttons up/down)

All shots saved in EEPROM (only 512 bytes, so max 256 shots... total)

Record (automatically) new shot, button to cancel (cancels current, hold down to erase) (one button)

Measurement statistics: ES (Extreme spread), AV (Average), SD (Standard deviation), NO (Number of shots in string)
(one button that cycles between these and shot display, new shot changes to shot display)

Button to erase all of memory. Only accessible when switching on.

Button to put device in printout mode where all memory is dumped to serial output. (Only accessible when switching on by
holding down button?)

Button to show how many shots fired/remaining?

Also, somwehow display gate voltage? We have HW to measure it. Need mode for that. Could be button to do that when switching
on as well.

Display:

First four digits are string and shot with decimal dot between them. Next four digits are result. When ES/SD/AV, then
shot number is replaced by these two digits.

Output format:

CSV with string no, shot no, m/s, and raw count. All calcs have to be redone in spreadsheet.

Design:

EEPROM first position, latest shot saved (just use index). Shot string "0" means erased. We'll just have to read through all
of memory when calculating average etc. Should be fast enough. We know the current string, so just select those and calculte.

Hmm, how to code shot speed and string? Not enough bits... Yes, if we assign 500 bytes, with one byte string, and two bytes
shot (maybe actually raw count?) we can fit 166 shots. That ought to be enough. That's 16 strings of ten. Or 32 of five.

*/

TM1638plus dp(A2, A1, A0); // Strobe, Clock, Data; And handles decima point, which TM1638lite doesn't

// Pinout

// 8 bits of the low or high counter (B counter only), inputs
// D=0 and D1 collide with USB (and other) serial!!!
const int Y0 = 3;
const int Y1 = 4;
const int Y2 = 5;
const int Y3 = 6;
const int Y4 = 7;
const int Y5 = 8;
const int Y6 = 9;
const int Y7 = 10;

// Register B high/low output control. Active low. Both high means tri-state
const int GBL = A4;
const int GBH = A5;

// Measure voltage level comparator threshold
const int VTHRESH = A6;

// Clock B enable, active low
//const int CLKBEN = A2;

// Clock clear, clears all registers, active low, and also clear the JK-flip flops
const int CCLR = A3;

// Register clock, rising edge clocks counter value into register for readout
const int RCLK = 11;

const int CRDY = 2; // Trigger interrupt on negative flank, means there's a result to read from counter

void on(int pin) { // Switch on (active low)
digitalWrite(pin, LOW);
}

void off(int pin) { // Switch pin off (active low)
digitalWrite(pin, HIGH);
}

volatile int ctr_rdy = 0; // Flag, set by control circuit when counter has result
void counter_ready() {
ctr_rdy = 1;
digitalWrite(RCLK, HIGH); // Strobe clock value into register for readout
}

void EE_clear_memory() { // Clear the memory
for (int i = 0; i < EEPROM.length(); ++i) EEPROM.write(i, 0);
EEPROM.write(0, 1); // Initialise pointer to next value to write
}

void EE_store(byte string, word count) { // Write new shot
byte shot = EEPROM.read(0); // Current place to store record {byte string, word count}
EEPROM.write(shot, string);
EEPROM.write(shot + 1, highByte(count));
EEPROM.write(shot + 2, lowByte(count));
EEPROM.write(0, shot + 3); // Update next shot pointer
#ifdef DEBUG
Serial.print("String: "); Serial.print(string); Serial.print(" Count: "); Serial.println(count);
Serial.print("Shot: "); Serial.print(shot); Serial.print(" Shot+1: "); Serial.print(shot + 1); Serial.print(" Shot+2: "); Serial.println(shot + 2);
Serial.print("New shot indx: "); Serial.println(shot + 3);
#endif

}

bool EE_shots() {
return EEPROM.read(0) > 1; // There are stored shots
}

void EE_update_last_string(byte string) {
if (EE_shots()) {
EEPROM.write(EEPROM.read(0) - 3, string);
}
}

byte current = 1; // Always point at the current hit
void EE_first(void) { // Reset search to beginning of EEPROM
current = 1;
}
void EE_last(void) { // Reset search to the end of EEPROM
current = EEPROM.read(0);
}
word EE_next(byte string) { // Return the next count value in string, zero if not found
#ifdef DEBUG
Serial.print("EE_next str: "); Serial.println(string);
#endif
if (current >= EEPROM.length()) { // We've run out of space aleady from the beginning
current = EEPROM.length();
return 0;
}
#ifdef DEBUG
Serial.println("EE_next enter loop");
#endif
while (EEPROM.read(current) != string) { // Note that string=0 means empty and hence cannot match
#ifdef DEBUG
Serial.print(".");
#endif
if (current >= EEPROM.read(0)) {
current = EEPROM.read(0);
#ifdef DEBUG
Serial.println("NOT FOUND");
#endif
return 0; // Not found
}
current += 3;
}
word ret_val = (EEPROM.read(current + 1) * 256 + EEPROM.read(current + 2)); // Hit
current += 3; // Set up for next hit
return ret_val;
}

word EE_prev(byte string) { // Return the previous count value in string, zero if not found
current -= 3;
if (current < 1) {
current = 1;
return 0; // No shots
}
while (EEPROM.read(current) != string) {
if (current < 1) {
current = 1; // Clamp
return 0; // Not found
}
current -= 3;
}
word ret_val = (EEPROM.read(current + 1) * 256 + EEPROM.read(current + 2)); // Hit
return ret_val;
}

void EE_erase_last() { // Erase last recorded shot if any
if (EE_shots()) {
byte shot = EEPROM.read(0);
shot -= 3; // Points to next free storage
EEPROM.write(shot, 0);
EEPROM.write(shot + 1, 0);
EEPROM.write(shot + 2, 0);
EEPROM.write(0, shot); // Now points to free space
}
}

byte EE_last_string() { // What's the string of the last recorded shot?
int shotindx = EEPROM.read(0);
#ifdef DEBUG
Serial.println(shotindx > 1 ? EEPROM.read(shotindx - 3) : 0);
#endif
return shotindx > 1 ? EEPROM.read(shotindx - 3) : 0; // Return zero if no shots recorded, so no string
}

int EE_shots_in_string(byte string) { // Count shots in current string
int i = 0;
EE_first();
while (EE_next(string)) ++i;
#ifdef DEBUG
Serial.print("String: "); Serial.println(string);
Serial.print("Sht in str: "); Serial.println(i);
#endif
return i;
}

long EE_shot_speed(byte string, int shot) { // Return speed in decimeters/second so we can print one decimal
word val;
EE_first();
for (int i = 0; i < shot; ++i) {
val = EE_next(string);
#ifdef DEBUG
Serial.print("EE_shot_speed val: "); Serial.println(val);
#endif
}
return (val == 0) ? 0 : 49880000L / val; // May be zero, which means, no shot, so display that instead
// 62.5ns cycle time, and 311.75 mm length gives constant above
}

long cnt_to_speed(word cnt) { // In decimeters/s
if (cnt != 0)
return 49880000L / cnt;
else
return 0;
}

double cnt_to_speed_d(word cnt) { // In decimeters/s
if (cnt != 0)
return 49880000.0 / double(cnt);
else
return 0.0;
}

char *dm2str(long speed) { // Convert speed in decimeters/second to 4 digits
static char buff[6]; // Null byte though not strictly needed, and need room for decimal point
#ifdef DEBUG
Serial.print("dm2str speed: "); Serial.println(speed);
Serial.print("buff: ");
#endif
if (speed > 9999) { // Can't fit in 4 places with one decimal so whole number
dtostrf(round(double(speed) / 10.0), 4, 0, buff); // Just four digits with no decimals
} else {
dtostrf(double(speed) / 10.0, 5, 1, buff); // 5=field width with 4 digits, 1 decimal point, and 1 decimal place
}
#ifdef DEBUG
Serial.println(buff);
#endif
return buff;
}


void setup() {
#ifdef DEBUG
Serial.begin(115000);
#endif
#ifdef DEBUG
for (int d_i = 0; d_i < EEPROM.length(); ++d_i) {
Serial.print(d_i);
Serial.print(":");
Serial.print(EEPROM.read(d_i));
Serial.print(" ");
}
Serial.println("");
#endif
// Initialise the pins
pinMode(Y0, INPUT_PULLUP);
pinMode(Y1, INPUT_PULLUP);
pinMode(Y2, INPUT_PULLUP);
pinMode(Y3, INPUT_PULLUP);
pinMode(Y4, INPUT_PULLUP);
pinMode(Y5, INPUT_PULLUP);
pinMode(Y6, INPUT_PULLUP);
pinMode(Y7, INPUT_PULLUP);

pinMode(VTHRESH, INPUT);
analogReference(INTERNAL); // Measure against the 1.1V internal ref for better accuracy
dp.reset();
int timeout = 0;
bool cleared_p = false;
while (dp.readButtons() == 1 << 0) { // First button for four seconds when starting clears all of memory
++timeout;
if (timeout < 40) {
dp.displayText("NUKING ");
} else {
dp.displayText("--GONE--");
if (!cleared_p) {
EE_clear_memory();
cleared_p = true;
}
}
delay(100);
}
timeout = 0; // We've cleared memory

if (dp.readButtons() == 1 << 1) { // Test mode! Store some values and some strings, 3 strings with 5 values each
EE_store(1, 5000);
EE_store(1, 5500);
EE_store(1, 4900);
EE_store(1, 4950);
EE_store(1, 5001);

EE_store(2, 6500);
EE_store(2, 6400);
EE_store(2, 6300);
EE_store(2, 6200);
EE_store(2, 6100);

EE_store(3, 10000);

EE_store(4, 5300);
EE_store(4, 5350);
EE_store(4, 5325);
EE_store(4, 5340);

EE_store(5, 4988); // 1000 m/s
EE_store(5, 2494); // 2000 m/s


dp.displayText("TESTDATA");
while (dp.readButtons()) delay(250); // Wait for user to let go of button
}

#ifdef DEBUG
for (int d_i = 0; d_i < EEPROM.length(); ++d_i) {
Serial.print(d_i);
Serial.print(":");
Serial.print(EEPROM.read(d_i));
Serial.print(" ");
}
Serial.println("");
for (int d_i = 1; d_i < EEPROM.length(); d_i += 3) {
Serial.print(d_i);
Serial.print(":");
Serial.print(EEPROM.read(d_i)); // String
Serial.print(",");
Serial.print((unsigned int)(256*EEPROM.read(d_i+1)+EEPROM.read(d_i+2))); // Value
Serial.print(" ");
}
Serial.println("");
#endif

if (dp.readButtons() == 1 << 5) { // Go into battery voltage measurement mode
dp.displayText("V THRESH");
while (dp.readButtons()) delay(250); // Wait for user to let go of button
while (!dp.readButtons()) { // Exit at next button press
unsigned int count = analogRead(VTHRESH);
char buff[10];
sprintf(buff, "%4d mV", int(count / 1024.0 * 1.1 * 1000)); // Ten bit ADC, integer arithmetic, result in mV
dp.displayText(buff);
delay(250);
}
}

if (dp.readButtons() == 1 << 6) { // Go into voltage threshold measurement mode
dp.displayText("V BATT");
while (dp.readButtons()) delay(250); // Wait for user to let go of button
while (!dp.readButtons()) { // Exit at next button press
long result; // From: https://code.google.com/archive/p/tinke ... meter.wiki
// 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
char buff[10];
sprintf(buff, "%4d mV", result); // Ten bit ADC, integer arithmetic, result in mV
dp.displayText(buff);
delay(250);
}
}

if (dp.readButtons() == 1 << 7) { // Print CSV, does straight ugly dump and own conversion
dp.displayText("DUMP CSV");
while (dp.readButtons()) delay(250); // Wait for user to let go of button
char buff[10];
for (int i = 20; i > 0; --i) { // Count down from 10
sprintf(buff, "-- %2d --", i);
dp.displayText(buff);
delay(1000); // Ten second count down
}
#ifndef DEBUG
Serial.begin(115200);
#endif
if (EE_shots()) {
dp.displayText("DATADUMP");
Serial.println("String, Speed (m/s), Count. (Divide 4988000 by count for exact speed.)"); // Print header
for (int i = 1; i < EEPROM.read(0); i += 3) { // Brutal dump!
byte string = EEPROM.read(i);
word count = EEPROM.read(i + 1) * 256 + EEPROM.read(i + 2);
double speed = 4988000.0 / double(count); // Direct conversion here, ugly...
Serial.print(string); Serial.print(", "); Serial.print(speed); Serial.print(", "); Serial.print(count); Serial.println();
}
} else {
dp.displayText("NO DATA");
}
while (true) ; // Hang here, restart is only option
#ifndef DEBUG
Serial.end();
#endif
}



pinMode(GBL, OUTPUT);
pinMode(GBH, OUTPUT);

pinMode(RCLK, OUTPUT);

//pinMode(CLKBEN, OUTPUT);

pinMode(CCLR, OUTPUT);

ctr_rdy = 0;
pinMode(CRDY, INPUT);
attachInterrupt(INT0, counter_ready, RISING);

// Set up counter
off(GBL);
off(GBH);
//off(CLKBEN);
on(CCLR);
delayMicroseconds(10);
off(CCLR);
digitalWrite(RCLK, LOW); // Don't clock register

dp.reset();
dp.displayText("-READY-");
delay(1000);
//dp.displayText(" ");
}

void blip() {
delayMicroseconds(1); // Get signals time to settle
}


// Variables
byte string = 1; // Shot string 1-99
byte shot = 1; // Shot in string 1-99
int buttons = 0; // Buttons value
int timeout = 0; // Counter for erase timeout
byte stat_state = 0; // State for AVG, SD etc. button
char buff[10]; // Output buffer

void loop() {
begin:
// Reset the clock
on(CCLR);
delayMicroseconds(10);
off(CCLR); // Pulse clear to clock
digitalWrite(RCLK, LOW); // And reset register clock (set in interrupt routine)

while (!ctr_rdy && !(buttons = dp.readButtons())) {
delay(10); // Wait for counter to have result
}

if (buttons != (1 << 7)) {
timeout = 0; // Reset timeout on all events that aren't last button
}
#ifdef DEBUG
Serial.println(buttons);
#endif
switch (buttons) {
case 1 << 0: // Previous string
--string;
string = max(string, 1);
string = min(string, 99);
shot = 1; // Reset to first shot of string
stat_state = 0; // Avg first if we switch
#ifdef DEBUG
Serial.println("BUTTON 0");
#endif
sprintf(buff, "%-2d%-2d%4s", string, shot, dm2str(EE_shot_speed(string, shot))); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
delay(250);
goto begin;
break;

case 1 << 1: // Next string
++string;
string = max(string, 1);
string = min(string, 99);
shot = 1;
stat_state = 0; // Avg first if we switch
#ifdef DEBUG
Serial.println("BUTTON 1");
#endif
sprintf(buff, "%-2d%-2d%4s", string, shot, dm2str(EE_shot_speed(string, shot))); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
delay(250);
goto begin;
break;

case 1 << 2: // Previous shot
--shot;
shot = max(shot, 1);
shot = min(shot, 99);
stat_state = 0; // Avg first if we switch
sprintf(buff, "%-2d%-2d%4s", string, shot, dm2str(EE_shot_speed(string, shot))); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
delay(250);
goto begin;
break;

case 1 << 3: // Next shot
++shot;
shot = max(shot, 1);
shot = min(shot, 99);
stat_state = 0; // Avg first if we switch
sprintf(buff, "%-2d%-2d%4s", string, shot, dm2str(EE_shot_speed(string, shot))); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
delay(250);
goto begin;
break;

case 1 << 4: // AV, SD, ES, HI, LO

if (stat_state == 0) {// AVerage
double avg = 0.0;
int i, a;
EE_first();
for (i = 0, a = 0, avg = 0.0; (a = EE_next(string)) != 0; ++i) {
avg += cnt_to_speed_d(a);
}
#ifdef DEBUG
Serial.print("sum: "); Serial.println(avg);
Serial.print("N "); Serial.println(i);
#endif
avg = round(avg / (i > 0 ? double(i) : 1.0)); // Don't divide by zero
sprintf(buff, "%-2dAV%4s", string, dm2str(avg)); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
} else if (stat_state == 1) { // SDdeviation
double avg = 0.0;
int i, a;
double sd = 0.0;
EE_first();
for (i = 0, a = 0, avg = 0.0; (a = EE_next(string)) != 0; ++i) {
avg += cnt_to_speed_d(a);
}

avg = avg / (i > 0 ? double(i) : 1.0); // Don't divide by zero
#ifdef DEBUG
Serial.print("Avg: "); Serial.println(avg);
#endif
EE_first();
for (i = 0, a = 0; (a = EE_next(string)) != 0; ++i) {
sd += (cnt_to_speed_d(a) - avg) * (cnt_to_speed_d(a) - avg);
#ifdef DEBUG
Serial.print("a-avg: "); Serial.println(cnt_to_speed_d(a) - avg);
Serial.print("running sum: "); Serial.println(sd);
#endif
}
#ifdef DEBUG
Serial.print("SUM: "); Serial.println(sd);
#endif
if (i > 1) {
sd = (1.0 / (i - 1.0)) * sd;
#ifdef DEBUG
Serial.print("SD double: "); Serial.println(sd);
#endif
sd = round(sqrt(sd));
} else {
sd = 0.0;
}
sprintf(buff, "%-2dSD%4s", string, dm2str(sd)); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
} else if (stat_state == 2) { // EStreme spread
int es;
EE_first();
long hi, lo, v;
hi = 0; lo = 99999;
while ( (v = cnt_to_speed(EE_next(string))) != 0) {
hi = max(hi, v);
lo = min(lo, v);
}
es = hi - lo;
sprintf(buff, "%-2dES%4s", string, dm2str(es)); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
} else if (stat_state == 3) {// HIgh
long hi, lo, v;
EE_first();
hi = 0; lo = 99999;
while ( (v = cnt_to_speed(EE_next(string))) != 0) {
hi = max(hi, v);
lo = min(lo, v);
}
sprintf(buff, "%-2dHI%4s", string, dm2str(hi)); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
} else if (stat_state == 4) { // LOw
long hi, lo, v;
EE_first();
hi = 0; lo = 99999;
while ( (v = cnt_to_speed(EE_next(string))) != 0) {
hi = max(hi, v);
lo = min(lo, v);
}

sprintf(buff, "%-2dLO%4s", string, dm2str(lo)); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
}
stat_state = (stat_state == 4) ? 0 : stat_state + 1; // Next time, next state
delay(250); // No bounces!
goto begin;
break;

case 1 << 5: // Decrese stored string of current shot
stat_state = 0; // Avg first if we switch
#ifdef DEBUG
Serial.println("Button 5");
#endif
if (EE_shots()) {
string = EE_last_string();
--string;
string = max(string, 1);
string = min(string, 99);
EE_update_last_string(string);
int last_string, last_shot;
last_string = EE_last_string();
last_shot = EE_shots_in_string(last_string);
sprintf(buff, "%-2d%-2d%4s", last_string, last_shot, dm2str(EE_shot_speed(last_string, last_shot)));
dp.displayText(buff); // Set the external display
}
delay(250);
goto begin;
break;

case 1 << 6: // Increase string of current shot
stat_state = 0; // Avg first if we switch
#ifdef DEBUG
Serial.println("Button 6");
#endif
if (EE_shots()) {
string = EE_last_string();
++string;
string = max(string, 1);
string = min(string, 99);
EE_update_last_string(string);
int last_string, last_shot;
last_string = EE_last_string();
last_shot = EE_shots_in_string(last_string);
sprintf(buff, "%-2d%-2d%4s", last_string, last_shot, dm2str(EE_shot_speed(last_string, last_shot)));
dp.displayText(buff); // Set the external display
}
delay(250);
goto begin;
break;

case 1 << 7: // Erase current shot, hold down for 4 seconds, short blip display last shot
stat_state = 0; // Avg first if we switch
#ifdef DEBUG
Serial.println("Button 7");
#endif
++timeout;
#ifdef DEBUG
Serial.print("Timeout: "); Serial.println(timeout);
#endif
if (timeout >= 16) { // We've held down for four seconds, so actually ERASE
EE_erase_last();
timeout = 0;
dp.displayText(" ERASED ");
buttons = 0; // Bugfix, don't erase all shots if first sensor gets unhooked
delay(2000);
}

int last_string, last_shot;
last_string = EE_last_string();
string = last_string != 0 ? last_string : 1; // Set current string
last_shot = EE_shots_in_string(last_string);
shot = last_shot != 0 ? last_shot : 1; // And current shot as well
EE_last();
word ee_prev = EE_prev(last_string);
sprintf(buff, "%-2d%-2d%4s", last_string, last_shot, dm2str(cnt_to_speed(ee_prev)));
#ifdef DEBUG
Serial.print("last_string: "); Serial.println(last_string);
Serial.print("last_shot: "); Serial.println(last_shot);
Serial.print("last value: "); Serial.println(ee_prev);
#endif
if (timeout >= 8) { // We've held down for two seconds, so warn! (Delay is 100ms so 20 x is two seconds
sprintf(buff, "-ERASE- ");
dp.displayText(buff);
}
dp.displayText(buff); // Set the external display
delay(250);
goto begin;
break;
}


// We have a bullet!
ctr_rdy = 0; // Reset result ready flag
blip();
digitalWrite(RCLK, LOW); // Reset RCLK, set in interrupt routine

word val = 0; // 16 bits unsigned
val = 0;

on(GBH);
blip();
for (int i = 10; i >= 3; --i) {
val = val << 1;
val |= digitalRead(i); // Read the pin number directly
}

off(GBH);

on(GBL);
blip();
for (int i = 10; i >= 3; --i) {
val = val << 1;
val |= digitalRead(i); // Read the pin number directly
}

off(GBL);

// Store shot in EEPROM
EE_store(string, val);
#ifdef DEBUG
Serial.print("SHOT val: "); Serial.println(val);
#endif
sprintf(buff, "%-2d%-2d%4s", string, EE_shots_in_string(string), dm2str(cnt_to_speed(val))); // 62.5ns cycle time, and 311.75 mm length gives constant above
dp.displayText(buff); // Set the external display
dp.setLED(4, 1); // Light LED to announce new shot!
dp.setLED(5, 1); // Light LED to announce new shot!
dp.setLED(6, 1); // Light LED to announce new shot!
dp.setLED(7, 1); // Light LED to announce new shot!
delay(500);
dp.setLED(4, 0);
dp.setLED(5, 0);
dp.setLED(6, 0);
dp.setLED(7, 0);
}
Användarvisningsbild
lasse-a
 
Inlägg: 1004
Blev medlem: 21 jun 2018, 08:19
Ort: Västergötland

Re: MagnetoSpeed mod ghetto

Inläggav lasse-a » 22 okt 2019, 14:37

Och här är lite bilder från senaste testerna av hela systemet. Med oscilloskop parallellt för verifiering.

Uppställning:

uppställning.jpg


Här lite närbilder på interfacet efter fem skott i serie två:

skott-2-5.jpg


Medelvärdet på de fem skotten i serie 2:

2-avg.jpg


Och spridningen:

2-es.jpg


Varför serie 2? Jo jag glömde trycka på rätt knapp på oscilloskopet första skottet, så det blev inte sparat.

Här en illustration på varför det är vanskligt att använda den här metoden. Vågformerna blir beroende på hur parallellt man lyckas montera sin skena:

SCR05.GIF


Här ser man tydligt att andra sensorn ligger lite längre ifrån kulbanan. Den har bara halva spänningen jämfört med den första. Då kan man inte se någon avvikelse med blotta ögat, utan monteringen ser ut som den alltid gjort. Men eftersom magnetfältet faller med kuben på avståndet, så behövs det inte mycket skillnad i avstånd för att det skall bli en stor skillnad i inducerad ström. (Gränsvärdet är satt till 250mV här. Vilket är i högsta laget).

Fick iaf bättre värden den här gången, dvs jag tror på dem. Har inte räknat på om de stämmer med de sparade datat dock. Det kommer senare.

Skulle även testat med 308-an. Men dels så tog ljuset slut, kan inte skjuta om jag inte ser vallen (och jag fick inte dit bilen), och dels så såg jag kanterna på mina sensorer när jag monterat och tittade genom pipan. Det vågar jag inte tro kommer sluta bra, så jag får göra en bättre montering hemma, under mera ordnade förhållanden, innan jag tror på det. (Finns också risk att jag blåser bort sensorerna, så kanske skall lägga till en bit plast framför).

Så, det börjar närma sig slutet iaf. Återstår analys och bestämning av felen, samt naturligtvis det ni alla väntat på kostnadsberäkninge! "Watch this space".
Du har inte de rättigheter som krävs för att öppna de filer som bifogats till detta inlägg.
Användarvisningsbild
lasse-a
 
Inlägg: 1004
Blev medlem: 21 jun 2018, 08:19
Ort: Västergötland

Re: MagnetoSpeed mod ghetto

Inläggav lasse-a » 22 okt 2019, 15:03

OK, nu när vi börjar närma oss slutet så kan vi ju svara på frågan alla undrar över? Hur mycket billigare än en riktig MagnetoSpeed blev det då? Det var ju därför vi började det här "Kan jag bygga en för två hundra" möttes ju av "Lycka till..." ;)

Så vad kostade grejjen då?

Det är lite svårt att räkna på eftersom jag hade en del grejjor hemma. Men låt oss börja summera:

Först sensorn. Hade magneterna, men de kan fås för 40:- styck. Hade också 3D skrivaren (så jag räknar inte den) men skrev ut plast för 5:- eller så. Köpte faktiskt aluminiumskenan för 50:- (dyraste enskilda komponenten), och hittade mässingsröret i skrotbingen. Sladden kom från någonstans i mina gömmor, det gjorde även tråden jag lindade med (gammal nätverkskabel). Men säg 20:- för det då.

Summa summarum: Sensorskena 155:-

Sedan "datorn".

Den är packad i en låda från Biltema (kunde skrivit ut, men orkade inte CADa en låda). Det är egentligen en eldosa som kostar 25:- Display osv. fås från Kina: för 14:- I lådan så sitter två återvunna 18650 celler av mycket bra kvalitet men skall man köpa billiga från Kina (vilket man inte skall) så fås de för 20:- Bättre är dock att ta dem från ett datorbatteri som "gett upp" eller dylikt. Hållaren i samma storleksordning. Så lådan och batterier: 70:-

Sedan till kretsarna på kortet: Arduinon är en kopia man får från Kina för 20:-. Kretsarna köpte jag faktiskt från Digikey (piggyback på en annan order) för att få garanterat bra grejjor. Komparator, 5:-, JK-vippa, 7:-, NAND, 5:-, räknaren 10:-, kristall 23:-. Sedan ett kretskort från högen, lite lödten, kablar, resistorer osv. Säg 10:- till. Summa kretskort: 80:-

(Allt lite lagom avrundat och med en dollarkurs om 10 SEK/$. Skall man köpa från Kina så får man ju dessutom lägga till lite för hantering från posten, om man inte kan belasta det på något annat. Det har jag gjort här eftersom grejjorna köptes innan det kom på tapeten)

Så det plockar på fortare än man tror...
Summa summarum: 155+70+80 = 305 Svenska pengar. Kom inte under 200:- mao. :( ;) Men väl under de 5000:- som det kostat att köpa en.

Nej, man skall ju inte räkna på en hobby, och min poäng från början var som sagt att MagnetoSpeed själva inte har denna kostnad, de kommer billigare undan.

Dock inte om de skall ha täckning för utvecklingskostnaden. Som ingenjör kostar man ju 700:- i timmen eller så, och jag har ju lätt lagt 30h. Så det blir ju 21000:- bara i den kostnaden. Nu spelar det ju ingen roll eftersom jag inte har någon möjlighet att jobba övertid (eller skulle vilja), så jag kan inte enkelt översätta den tiden i pengar. Och det är ju som sagt en hobby. Jag går lika lite plus på den som jag går på mitt jagande för den delen. (Kunde blivit mycket fläskfilé för de pengarna... Därmed inte sagt att andra inte gör det och går plus). För att inte tala om båten... Där kunde man ju lika gärna stått i sjöstället i duschen och rivit sönder femhundringar... ;)

Att ha gjort det själv, bringar mig dock mycket glädje som skall sättas på pluskontot. Om det var värt 20000+ det vete f-n dock... ;)
Användarvisningsbild
lasse-a
 
Inlägg: 1004
Blev medlem: 21 jun 2018, 08:19
Ort: Västergötland

Föregående

Återgå till Projekthörnan

Vilka är online

Användare som besöker denna kategori: Inga registrerade användare och 2 gäster