Hallo zusammen
Ich habe mir eine Kiste gebaut und wollte eine Fussbal-Schussgeschwindigkeits-Anlage.
Ich habe folgende Teile verbaut:
- Arduino UNO
- Display Matrix HUB08
- 3x Gravity Digital Microwave Sensor Radar- / Geschwindigkeitssensor
Es funktioniert eigentlich, da ich aber die Kiste hinter das Tor stellen will (mechanisch gegen den Ball geschützt), stört das Tornetz. Die Radar-Senoren gehen nicht mehr.
Meine Frage, was kann ich für Sensoren nehmen, die funktionieren, auch bei einem bewegendem Tornetz dazwischen?
Dieser Code ist in Millisekunden, weil ich mit der Hand testen wollte.
// ---------- Arduino UNO R3 – HUB08 64x32 Panel ----------
// Hand-/Ball-Test mit 3x Gravity Digital Radar an A0/A1/A2
// Frequenzmessung per Pin-Change-Interrupts (keine Blockierung).
// Anzeige: 0–999 (≈ Hz), 10 s halten, dann wieder 000.
// Ziffern 20x32, 2 Pixel nach unten verschoben. Keine Blinkerei.
#define COL_PIXEL 64
#define ROW_PIXEL 32
// HUB08-Pins (wie bei dir)
int latchPin = 8; // LT
int clockPin = 12; // SK
int data_R1 = 11; // R1
int data_R2 = 7; // R2
int en_74138 = 2; // OE (LOW = an)
int la_74138 = 3;
int lb_74138 = 4;
int lc_74138 = 5;
int ld_74138 = 6;
#define BRIGHT_US 120 // kleiner = dunkler
#define Y_OFFSET 2 // Ziffern 2 Pixel nach unten
#define HOLD_MS 10000 // 10 s halten
// -------- Sensoren --------
// A0..A2 liegen am PCINT-Block 1 (Port C)
const uint8_t S0 = A0; // PCINT8 (PINC0)
const uint8_t S1 = A1; // PCINT9 (PINC1)
const uint8_t S2 = A2; // PCINT10 (PINC2)
const bool ACTIVE_HIGH = true; // falls deine Module LOW-aktiv sind -> false
// Messfenster (kurz halten, damit Ball-Impulse sicher gezählt werden)
#define WINDOW_MS 20 // Integrationszeit pro Auswertung (~20 ms)
#define QUIET_MS 30 // Ruhezeit ohne neue Auswertung, verhindert Dauerfeuer
// ---------- Framebuffer & Ziffern ----------
uint8_t fb[32][8];
const byte digits[10][16] PROGMEM = {
{0x3C,0x7E,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7E,0x3C,0x00,0x00}, //0
{0x18,0x38,0x78,0xD8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00}, //1
{0x7E,0xFF,0xC3,0x03,0x07,0x0E,0x1C,0x38,0x70,0xE0,0xC0,0xC3,0xFF,0xFF,0x00,0x00}, //2
{0x7E,0xFF,0xC3,0x03,0x07,0x1E,0x1E,0x07,0x03,0x03,0xC3,0xFF,0x7E,0x00,0x00,0x00}, //3
{0x06,0x0E,0x1E,0x36,0x66,0xC6,0x86,0xFF,0xFF,0x06,0x06,0x06,0x06,0x00,0x00,0x00}, //4
{0xFF,0xFF,0xC0,0xC0,0xFE,0xFF,0xC3,0x03,0x03,0x03,0xC3,0xFF,0x7E,0x00,0x00,0x00}, //5
{0x3E,0x7F,0xE3,0xC0,0xC0,0xFE,0xFF,0xC3,0xC3,0xC3,0xC3,0x7F,0x3E,0x00,0x00,0x00}, //6
{0xFF,0xFF,0x03,0x06,0x06,0x0C,0x18,0x30,0x30,0x60,0x60,0x60,0x60,0x00,0x00,0x00}, //7
{0x7E,0xFF,0xC3,0xC3,0xC3,0x7E,0x7E,0xC3,0xC3,0xC3,0xC3,0xFF,0x7E,0x00,0x00,0x00}, //8
{0x7E,0xFF,0xC3,0xC3,0xC3,0xC3,0xFF,0x7F,0x03,0x03,0xC7,0xFE,0x7C,0x00,0x00,0x00} //9
};
void clearFB(){ for(int y=0;y<32;y++) for(int x=0;x<8;x++) fb[y][x]=0; }
inline void putPixRaw(int x,int y){ if((unsigned)x<64 && (unsigned)y<32) fb[y][x>>3] |= (1<<(7-(x&7))); }
inline void putPix(int x,int y){ putPixRaw(x, y+Y_OFFSET); }
void putDigitWide20x32(int xOff, int d){
static const uint8_t rep[8] = {3,2,3,2,3,2,3,2}; // Summe 20
if(d<0 || d>9) return;
int x = xOff;
for (int col=0; col<8; col++){
for (int row=0; row<16; row++){
byte line = pgm_read_byte(&digits[d][row]);
if(line & (1<<col)){ putPix(x, row*2+0); putPix(x, row*2+1); }
}
for (int r=1; r<rep[col]; r++){
for (int row=0; row<16; row++){
byte line = pgm_read_byte(&digits[d][row]);
if(line & (1<<col)){ putPix(x+r, row*2+0); putPix(x+r, row*2+1); }
}
}
x += rep[col];
}
}
void drawNumber(int num){
if(num<0) num=0; if(num>999) num=999;
clearFB();
int h = (num/100)%10;
int z = (num/10)%10;
int e = num%10;
// Reihenfolge links->rechts = 123 (Hundert, Zehn, Eins)
putDigitWide20x32(44, h);
putDigitWide20x32(22, z);
putDigitWide20x32( 0, e);
}
void shiftRow(uint8_t r){
for (int b=7;b>=0;--b){
uint8_t top = fb[r][b];
uint8_t bot = fb[r+16][b];
for (uint8_t k=0;k<8;k++){
digitalWrite(data_R1,(top & (1<<k))?LOW:HIGH);
digitalWrite(data_R2,(bot & (1<<k))?LOW:HIGH);
digitalWrite(clockPin,LOW); digitalWrite(clockPin,HIGH);
}
}
}
void scanOnce(){
for (uint8_t rr=0; rr<16; rr++){
uint8_t r = 15-rr;
digitalWrite(en_74138,HIGH);
shiftRow(r);
digitalWrite(latchPin,HIGH); digitalWrite(latchPin,LOW);
digitalWrite(la_74138, r&1);
digitalWrite(lb_74138,(r>>1)&1);
digitalWrite(lc_74138,(r>>2)&1);
digitalWrite(ld_74138,(r>>3)&1);
digitalWrite(en_74138,LOW);
delayMicroseconds(BRIGHT_US);
digitalWrite(en_74138,HIGH);
}
}
// ---------- Interrupt-Zähler ----------
volatile uint8_t lastPINC;
volatile uint16_t cnt0=0,cnt1=0,cnt2=0;
ISR(PCINT1_vect){ // Port C (A0..A5)
uint8_t now = PINC; // momentaner Portzustand
uint8_t changed = now ^ lastPINC;
uint8_t mask = (1<<PC0)|(1<<PC1)|(1<<PC2); // A0,A1,A2
changed &= mask;
// Flankenerkennung je nach Aktiv-Logik
// Wir zählen standardmäßig RISING (0->1). Bei ACTIVE_HIGH=false zählen wir FALLING (1->0).
if(changed & (1<<PC0)){
bool rising = ((lastPINC & (1<<PC0))==0) && ((now & (1<<PC0))!=0);
bool falling= ((lastPINC & (1<<PC0))!=0) && ((now & (1<<PC0))==0);
if( (ACTIVE_HIGH && rising) || (!ACTIVE_HIGH && falling) ) cnt0++;
}
if(changed & (1<<PC1)){
bool rising = ((lastPINC & (1<<PC1))==0) && ((now & (1<<PC1))!=0);
bool falling= ((lastPINC & (1<<PC1))!=0) && ((now & (1<<PC1))==0);
if( (ACTIVE_HIGH && rising) || (!ACTIVE_HIGH && falling) ) cnt1++;
}
if(changed & (1<<PC2)){
bool rising = ((lastPINC & (1<<PC2))==0) && ((now & (1<<PC2))!=0);
bool falling= ((lastPINC & (1<<PC2))!=0) && ((now & (1<<PC2))==0);
if( (ACTIVE_HIGH && rising) || (!ACTIVE_HIGH && falling) ) cnt2++;
}
lastPINC = now;
}
// Laufende Auswertung
unsigned long winStart=0, lastShownAt=0;
bool holding=false;
int shownValue=0;
void beginWindow(){
noInterrupts();
cnt0=cnt1=cnt2=0;
interrupts();
winStart = millis();
}
bool windowReady(){
return (millis()-winStart) >= WINDOW_MS;
}
int readWindowHz(){
// Kopieren atomar
uint16_t c0,c1,c2;
noInterrupts();
c0=cnt0; c1=cnt1; c2=cnt2;
interrupts();
// “Beste” Spur wählen (max Counts)
uint16_t best = c0;
if(c1>best) best=c1;
if(c2>best) best=c2;
// Counts / Zeitfenster → Hz
// Achtung: Gravity-Digital gibt oft schon "Takt" aus (nicht reine Doppler-Sinus), daher ist das Hz hier ein relativer Wert.
float hz = (best * 1000.0f) / (float)WINDOW_MS;
int val = (int)(hz + 0.5f);
if(val>999) val=999;
return val;
}
void setup(){
// Display
pinMode(latchPin,OUTPUT);
pinMode(clockPin,OUTPUT);
pinMode(data_R1,OUTPUT);
pinMode(data_R2,OUTPUT);
pinMode(en_74138,OUTPUT);
pinMode(la_74138,OUTPUT);
pinMode(lb_74138,OUTPUT);
pinMode(lc_74138,OUTPUT);
pinMode(ld_74138,OUTPUT);
digitalWrite(en_74138,HIGH);
// Sensorpins als Input
pinMode(S0, INPUT);
pinMode(S1, INPUT);
pinMode(S2, INPUT);
// Pin-Change-Interrupts für Port C aktivieren
// PCICR – Pin Change Interrupt Control Register
PCICR |= (1<<PCIE1); // PCINT[14:8] (Port C) aktiv
// Maske setzen: PCINT8..10 (A0..A2)
PCMSK1 |= (1<<PCINT8) | (1<<PCINT9) | (1<<PCINT10);
lastPINC = PINC; // Startzustand merken
interrupts();
drawNumber(0); // Startbild 000
beginWindow();
}
void loop(){
// Display läuft immer
scanOnce();
unsigned long now = millis();
if(holding){
// Haltephase aktiv?
if(now - lastShownAt >= HOLD_MS){
holding=false;
shownValue=0;
drawNumber(0);
// kleine Ruhezeit, dann neues Fenster
if(now - winStart >= QUIET_MS) beginWindow();
}
return;
}
// Fenster fertig?
if(windowReady()){
int val = readWindowHz();
// Schwellwert: ignorier Kleinkram (z.B. Netz- oder Vibrations-Echos)
if(val >= 5){ // ggf. 3..10 probieren
shownValue = val;
drawNumber(shownValue);
lastShownAt = now;
holding = true;
}else{
// zu wenig – neues Fenster
beginWindow();
}
}
}
