4.2.17

Wiegand - next try - mit Arduino

Anhand dieser klasse Anleitung hatte ich endlich Erfolg.
Ich konnte den Code der RFIDs auslesen.
Zuvor war ich mit Pidoorman und dem Raspberry leider gescheitert.
Die Codes wurden nicht erkannt.

Nun aber mit dem Arduino.

Schaltung:

D0 Wiegand an D2 Arduino (D0 am DK-2872 => Blau)
D1 Wiegand an D3 Arduino (D1 => Gelb)
GND Wiegand and GND Arduino (GND => Schwarz)

Software von der o.a. Webseite.
HandleCodes.ino
/*
  Wiegand Door Controller
  Stefan Thesen, 2014
  Copyright: public domain -> do what you want
 
  For details visit http://blog.thesen.eu
 
  some code ideas / parts taken from Daniel Smith, www.pagemac.com
*/

#include <avr/eeprom.h>


////////////////////
// the setup routine
////////////////////
void setup()

  Serial.begin(9600);
  Serial.println(F("Wiegand Controller - S.T. 2014"));

  SetupCodeHandling();
  WiegandSetup();
  SetupUI();
 
  Serial.println(F(""));
  PrintMainMenu();
}


///////////////////////////////////////////
// main loop - handles reception & decoding
///////////////////////////////////////////
void loop()
{
  unsigned long ulFacilityCode, ulCardCode;
 
  // we have data?
  if (WiegandAvailable())
  {
    unsigned char i;
   
    Serial.print(F("Read "));
    Serial.print(WiegandBitCount());
    Serial.print(F(" bits. "));
   
    ulFacilityCode = WiegandFacilityCode();
    ulCardCode = WiegandCardCode();
    if (WiegandBitCount()!=0)
    {
      Serial.print(F("FacilityCode,CardCode = "));
      Serial.print(ulFacilityCode);
      Serial.print(F(","));
      Serial.println(ulCardCode);
      HandleCode(ulFacilityCode, ulCardCode);
    }
    else
    {
      Serial.println(F("Cannot decode. Unknown format."));
    }
   
    // clean up and get ready for the next card
    WiegandReset();
  }
}
UI.ino
/*
  User Interface Routines for Wiegand controller
  Stefan Thesen, 2014
  Copyright: public domain -> do what you want
*/

#include <avr/wdt.h>

//////////
// Globals
//////////

// note: Arduino String Objects eat (lots) of RAM dynamically and are not easy to predict
// so we do standard c 'strings', so that we can control memory
#define MAXSERBUFF 40                // max size for serial line in input
char sInBuffer[MAXSERBUFF];          // buffer for serial reading
int iMenuMode;                       // Mode flag for menu


void SetupUI()
{
  // init variables
  iMenuMode = 0;
  sInBuffer[0]=0;
}


/////////////////////////////////////////////////
// read serial input data and create input buffer
/////////////////////////////////////////////////
void serialEvent()
{
  char cc=0;
  if(Serial.available())
  {
    cc = Serial.read();
    if(cc==10||cc==13)
    {
       Serial.println(F(""));
       ProcessBuffer();
    }
    else
    {
      Serial.print(cc);
      int ipos = strlen(sInBuffer);
      if (ipos<MAXSERBUFF-1)
      {
        sInBuffer[ipos]=cc;
        sInBuffer[ipos+1]=0;
      }
    }
  }
}


//////////////////////////////////
// trigger actions by serial input
//////////////////////////////////
void ProcessBuffer()
{
  if (iMenuMode==0)
  {
    if (strcmp(sInBuffer,"1") == 0)
    {
      Serial.println(F("To add code/card enter: <Name,FacilityCode,CardCode,Action> as list or just <enter> to cancel"));
      Serial.print(F(">"));
      iMenuMode=1;
    }
    else if (strcmp(sInBuffer,"2") == 0)
    {
      Serial.println(F("To delete a code/card enter: Name"));
      Serial.print(F(">"));
      iMenuMode=2;
    }
    else if (strcmp(sInBuffer,"3") == 0)
    {
      // display all codes
      int ii;
      Serial.println(F("Codelist - Format: Name,FacilityCode,CardCode,Action"));
      for (ii=0;ii<iCodeListSize;ii++)
      {
        Serial.print(pCodeList[ii].sName);
        Serial.print(F(","));
        Serial.print(pCodeList[ii].ulFacilityCode);
        Serial.print(F(","));
        Serial.print(pCodeList[ii].ulCardCode);
        Serial.print(F(","));
        Serial.print(pCodeList[ii].bAction);
        Serial.println(F(""));
      }
      Serial.println(F("Codelist End."));
     
      iMenuMode=0;
    }
    else if (strcmp(sInBuffer,"7") == 0)
    {
      // reboot
      SetTamperFlag(false);
      asm volatile ("  jmp 0");
    }
    else if (strcmp(sInBuffer,"8") == 0)
    {
      // save to eeprom
      Serial.println(F("Saving to EEprom..."));
      SaveToEEProm();
      iMenuMode=0;
    }
    else if (strcmp(sInBuffer,"999") == 0)
    {
      // delete all codes
      iCodeListSize=0;
      Serial.println(F("Erasing EEprom..."));
      SaveToEEProm();
      iMenuMode=0;
    }
  }
  // ADD CODES AS CSV LIST
  else if (iMenuMode==1)
  {
    if (strlen(sInBuffer)==0)
    {
      iMenuMode=0;
    }
    else if (iCodeListSize<iMaxCodeList)
    {
      // find comma delimiters
      char *cFirstComma=NULL,*cSecondComma=NULL,*cThirdComma=NULL;  // set all to return code fail
      cFirstComma=strchr(sInBuffer,',');
      if (cFirstComma) cSecondComma=strchr(cFirstComma+1,',');
      if (cSecondComma) cThirdComma=strchr(cSecondComma+1,',');
      if (cFirstComma!=NULL && cSecondComma!=NULL && cThirdComma!=NULL)
      {
        pCodeList[iCodeListSize].ulFacilityCode = atol(cFirstComma+1);
        pCodeList[iCodeListSize].ulCardCode     = atol(cSecondComma+1);
        pCodeList[iCodeListSize].bAction        = atoi(cThirdComma+1);
       
        // name is max MAXNAMESIZE bytes incl termination char
        if ((cFirstComma-sInBuffer)>=MAXNAMESIZE) {cFirstComma=sInBuffer+MAXNAMESIZE-1;}
        memcpy((void*)pCodeList[iCodeListSize].sName,(void*)sInBuffer,cFirstComma-sInBuffer);
        pCodeList[iCodeListSize].sName[cFirstComma-sInBuffer]=0; // to be sure: term char
       
        // do not display status - otherwise arduino is too slow, when pasting a list
        //Serial.print(F("Entry added "));
        //Serial.print(pCodeList[iCodeListSize].sName);
        //Serial.println(F(". Next entry or hit Enter to exit."));
        //Serial.print(F(">"));
        iCodeListSize++;
      }
      else
      {
        Serial.println(F("Invalid entry! - Retry or hit Enter to exit."));
        Serial.print(F(">"));
      }
    }
    else
    {
      Serial.println(F("Maximum number of code entries reached! - Exiting."));
      iMenuMode=0;
    }   
  } 
  else if (iMenuMode==2)
  {
    // delete a code
    int ii=0;
    bool bFound=false;
    while (ii<iCodeListSize && !bFound)
    {
      if (strncmp(sInBuffer,pCodeList[ii].sName,MAXNAMESIZE)==0) {bFound=true;}
      else { ii++; }
    }
    if (bFound)
    {
      int ikill=ii;
      iCodeListSize--;
      for(ii=ikill;ii<iCodeListSize;ii++)
      {
        memcpy((void*)&pCodeList[ii],(void*)&pCodeList[ii+1],sizeof(CODELIST));
      }
      Serial.println(F("Entry removed."));
    }
    else
    {
      Serial.println(F("Entry not found!"));
    }
    iMenuMode=0;
  }
 
  // prepare for next inputs
  sInBuffer[0]=0;
  if (iMenuMode==0) {PrintMainMenu();}
}


///////////////////////
// prints the main menu
///////////////////////
void PrintMainMenu()
{
  Serial.println(F("***** MENU ****"));
  Serial.println(F("<enter> -> print this menu"));
  Serial.println(F("  1     -> learn a code / paste csv-list of codes"));
  Serial.println(F("  2     -> delete a code"));
  Serial.println(F("  3     -> display all codes"));
  Serial.println(F("  7     -> reset tamper flag & reboot"));
  Serial.println(F("  8     -> save all codes to eeprom"));
  Serial.println(F("  999   -> delete ALL codes in eeprom"));
  Serial.print  (F("Notes: Name entries max "));
  Serial.print  (MAXNAMESIZE-1);
  Serial.println(F(" characters."));
  Serial.println(F("       Action entries 1 for open, 0 for close."));
  Serial.println(F(""));
  Serial.print  (F(">"));
}
Wiegand_Controller.ino
/*
  Wiegand Door Controller
  Stefan Thesen, 2014
  Copyright: public domain -> do what you want
 
  For details visit http://blog.thesen.eu
 
  some code ideas / parts taken from Daniel Smith, www.pagemac.com
*/

#include <avr/eeprom.h>


////////////////////
// the setup routine
////////////////////
void setup()

  Serial.begin(9600);
  Serial.println(F("Wiegand Controller - S.T. 2014"));

  SetupCodeHandling();
  WiegandSetup();
  SetupUI();
 
  Serial.println(F(""));
  PrintMainMenu();
}


///////////////////////////////////////////
// main loop - handles reception & decoding
///////////////////////////////////////////
void loop()
{
  unsigned long ulFacilityCode, ulCardCode;
 
  // we have data?
  if (WiegandAvailable())
  {
    unsigned char i;
   
    Serial.print(F("Read "));
    Serial.print(WiegandBitCount());
    Serial.print(F(" bits. "));
   
    ulFacilityCode = WiegandFacilityCode();
    ulCardCode = WiegandCardCode();
    if (WiegandBitCount()!=0)
    {
      Serial.print(F("FacilityCode,CardCode = "));
      Serial.print(ulFacilityCode);
      Serial.print(F(","));
      Serial.println(ulCardCode);
      HandleCode(ulFacilityCode, ulCardCode);
    }
    else
    {
      Serial.println(F("Cannot decode. Unknown format."));
    }
   
    // clean up and get ready for the next card
    WiegandReset();
  }
}
WiegandLowLevel.ino
/*
  Wiegand Routines
  Stefan Thesen, 2014
  Copyright: public domain -> do what you want
*/

//////////
// Globals
//////////
#define MAX_BITS 50                  // max number of wiegand bits
#define WEIGAND_WAIT_TIME_MS 50      // milliseconds to wait for another weigand pulse. 

unsigned char pDataBits[MAX_BITS];   // stores all of the data bits
int iBitCount;                       // number of bits currently captured
bool bflagDone;                      // goes low when data is currently being captured
unsigned long ullast_weigand_event_ms; // countdown until we assume there are no more bits
bool bWiegandAvail;                  // true, if we are receiving

unsigned long ulFacilityCode=0;      // decoded facility code
unsigned long ulCardCode=0;          // decoded card code


///////////////////////////////////////////////////
// handle falling edge on Wiegand D0 line (bit = 0)
///////////////////////////////////////////////////
void WiegandD0()
{
  if(bWiegandAvail)
  {
    if (iBitCount<MAX_BITS)
    {
      pDataBits[iBitCount] = 0;
      iBitCount++;
    }
    bflagDone=false;
    ullast_weigand_event_ms = millis();  
  }
}


///////////////////////////////////////////////////
// handle falling edge on Wiegand D1 line (bit = 1)
///////////////////////////////////////////////////
void WiegandD1()
{
  if(bWiegandAvail)
  {
    if (iBitCount<MAX_BITS)
    {
      pDataBits[iBitCount] = 1;
      iBitCount++;
    }
    bflagDone=false;
    ullast_weigand_event_ms = millis();  
  }
}


////////////////////////////////////
// check if Wiegand signal available
////////////////////////////////////
bool WiegandAvailable()
{
  // there is no end to a command in the Wiegand protocol. So we wait till some time after the last pulse has passed.
  if (!bflagDone)
  {
    if ( (millis() - ullast_weigand_event_ms) > WEIGAND_WAIT_TIME_MS )
    {
      bflagDone = true;
      bWiegandAvail = false;
      if (iBitCount>0)
      {
        if(WiegandDecode())
        {
          return(true);
        }
        else
        {
          WiegandReset();
          return(false);
        }
      }
    }
  }
 
  return(false);
}


/////////////////////////////////////
// reset all Wiegand reader variables
/////////////////////////////////////
void WiegandReset()
{
  // clean up and get ready for the next card
  iBitCount = 0;
  bflagDone = true;
  bWiegandAvail = true;
  ulFacilityCode = 0;
  ulCardCode = 0;
}


////////////////////////////////
// setup Wiegand Reader on D2/D3
////////////////////////////////
void WiegandSetup()
{
  pinMode(2, INPUT);     // Wiegand D0
  pinMode(3, INPUT);     // Wiegand D1
 
  // Wiegand Read functions triggered by falling edge of D2 and D3
  attachInterrupt(0, WiegandD0, FALLING); 
  attachInterrupt(1, WiegandD1, FALLING);

  // init variables
  ullast_weigand_event_ms = 0;
  WiegandReset();
}


//////////////////////////////////////////////////////
// Decode facility and card-code from Wiegand raw data
//////////////////////////////////////////////////////
bool WiegandDecode()
{
  // different formats allocate bits differently to facility and card-code
  // define decoding based on BitCount
  // this is not perfect there are sometimes multiple standards for one BitCount
  int i;
  int ilowfac=0,ihighfac=0;
  int ilowcard=0,ihighcard=0;
 
  switch (iBitCount)
  {
    case 18:  // sebury proprietary ring indication
      ilowfac  = 0;  ihighfac  = 0;
      ilowcard = 1;  ihighcard = 17;
      break;
    case 26:  // standard 26 bit format
      ilowfac  = 1;  ihighfac  = 9;
      ilowcard = 9;  ihighcard = 25;
      break;
    case 35:  // 35 bit HID Corporate 1000 format
      ilowfac  = 2;  ihighfac  = 14;
      ilowcard = 14; ihighcard = 34;
      break;
    case 37: 
      // HID 37 bit standard / H10304
      //ilowfac  = 1;  ihighfac  = 17;
      //ilowcard = 17;  ihighcard = 36;
      // my way: get as much as possible into cardcode --> 4 bytes
      ilowfac  = 1;  ihighfac  = 12;
      ilowcard = 12; ihighcard = 36;
      break;
    default:
      Serial.print("Unknown format. Cannot decode ");
      Serial.print(iBitCount);
      Serial.println(" bit format.");
      return (false);
      break;
  }

  // facility code
  for (i=ilowfac; i<ihighfac; i++)
  {
     ulFacilityCode <<=1;
     ulFacilityCode |= pDataBits[i];
  }
 
  // card code
  for (i=ilowcard; i<ihighcard; i++)
  {
     ulCardCode <<=1;
     ulCardCode |= pDataBits[i];
  }
 
  return(true);
}


////////////////////////////
// get decoded Facility Code
////////////////////////////
unsigned long WiegandFacilityCode()
{
  return ulFacilityCode;
}


////////////////////////////
// get decoded Card Code
////////////////////////////
unsigned long WiegandCardCode()
{
  return ulCardCode;
}


////////////////////////////
// get decoded Card Code
////////////////////////////
int WiegandBitCount()
{
  return iBitCount;
}

Keine Kommentare:

Kommentar veröffentlichen

Openhab und Ecoflow Max - API Anbindung

 Ich wollte die neu erworbene Powerstation in Openhab einbinden, um den aktuellen Status (Ladestand etc.) über Openhab auswerten zu können. ...