Ball-Schussgeschwindigkeit

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();
    }
  }
}

Ein Radar-Sensor dürfte nicht von einem Netz aus Kunststoff beeinträchtigt werden.
Alle Isolationsmaterialien sollte Radar nicht reflecktieren oder abschwächen.

Grüße Uwe

Und wo müsste ich dan suchen?

Wenn ich mir vor dem Sensor (3Stück) bewege oder mit der Hand richtig Sensor fahre oder die ganze Kiste bewege, reagiert er, Wir am Display angezeigt.

Wenn das Netz dazwischen ist, reagiert er auf nichts!

Ich würde es mal mit einem "besseren" Sensor testen.

  1. Weil dieser mit einer zugelassenen Frequenz arbeitet.
  2. Weil hier die Qualität deutlich besser ist.

Im Netz könnten Metallfäden eingearbeitet sein. Probier mal ein anderes Material. Zur Not nimm ein altes Betttuch.

Kennt jemand der OmniPreSense OPS-241?

Habe VDD5V (bringe ich 4.8V vom Nano)

GND (alles zusammen)

Und möchte auf TXD die Km/h auslessen.

Auf TXD habe ich aber immer 0V!

OPS-241 per USB programmiert, im Seriellen Monitor habe ich Bewegungswerte zwischen -0.22 bis 1.53 (je nach Bewegung)

Damit ich per Nano ESP die Km/h auslesen kann, brauche ich ja auf TXD ein 3.3v (Ruhezustand) habe aber immer 0v

Danke für eure Hilfe

Kannst Du ein Datenblatt zeigen?

https://www.mouser.ch/new/omnipresense/omnipresense-ops241a-srr/?srsltid=AfmBOor0oLX-rTz1jL8sdGZ9771eKmyccnNwsIHriL6dnUdcy7y6oYc2

Ich sprach von Datenblatt. Den Shop kenn ich. Und das, was da als "Datenblatt" beworben wird ist ein Werbeflyer.

Suchmaschine defekt? :wink:

Hilfreich zum Programmieren wird dann vielleicht auch noch die im Datasheet erwähnte Application Note AN-010 (PDF) sein. Da steht drin, was man dem Sensor über die serielle Schnittstelle schicken muss, damit er so oder anders arbeitet. Kann man auch mit PuTTY oder so ausprobieren.