RFID membership card activated locker door

12 Likes

The reader is from Aliexpress (was $US9.20 during 11.11 sale, lets buy more next sale) 17.44C$ |Free shipping PN532 NFC RFID module User Kits for Arduino compatible|modul|module pvmodule gps - AliExpress Is connected via SPI using the ICSP connector.

The motor shield is a copy I made of the Adafruit v2 motor shield Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit [v2.3] : ID 1438 : $19.95 : Adafruit Industries, Unique & fun DIY electronics and kits uses i2c.

The code is very basic. One we have a card database my goal is to turn the locker into a basic vending machine for smd parts. Iā€™ll add wifi/ethernet somehow to do this.

#include <SPI.h>
#include <Adafruit_PN532.h>

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

static const uint8_t tomcard[] = {0x4, 0x2, 0x63, 0xEA, 0x50, 0x49, 0x81};

static const uint8_t limit_pin = 6;
static const uint8_t pn532_ss_pin = 10;

Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 
Adafruit_StepperMotor *myMotor = AFMS.getStepper(200, 2);

Adafruit_PN532 nfc(pn532_ss_pin);


static bool dooropen = true;

#define DEBUG 0

void lock()
{
    if (!dooropen) return;
    
    while (!digitalRead(limit_pin))
    {
        myMotor->onestep(BACKWARD, DOUBLE);
        delay(40);
    }
    myMotor->step(3, FORWARD, DOUBLE);
    myMotor->release();
    dooropen = false;
}

void unlock()
{
    if (dooropen) return;
    myMotor->step(50, FORWARD, DOUBLE);
    myMotor->release();
    dooropen = true;
}


void setup()
{
#if DEBUG
    Serial.begin(9600);
    while (!Serial);
    Serial.println("setup()");
#endif
    pinMode(limit_pin, INPUT);

    AFMS.begin();  // create with the default frequency 1.6KHz
    myMotor->setSpeed(30);  // 30 rpm

    nfc.begin();

    uint32_t versiondata = nfc.getFirmwareVersion();
    if (! versiondata)
    {
#if DEBUG      
        Serial.print("Didn't find PN53x board");
#endif    
        unlock();
        while (1); // halt
    }

#if DEBUG
    Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX); 
    Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC); 
    Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
#endif

    // Set the max number of retry attempts to read from a card
    // This prevents us from waiting forever for a card, which is
    // the default behaviour of the PN532.
    nfc.setPassiveActivationRetries(0xFF);
  
    // configure board to read RFID tags
    nfc.SAMConfig();

#if DEBUG  
    Serial.println("Waiting for an ISO14443A card");
#endif

    lock();
}


void loop()
{
    uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };	// Buffer to store the returned UID
    uint8_t uidLength;				// Length of the UID (4 or 7 bytes depending on ISO14443A card type)
  
    bool success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, &uid[0], &uidLength);
    if (success)
    {

        if (uidLength == sizeof(tomcard) && !memcmp(tomcard, uid, sizeof(tomcard)))
        {
            if (dooropen)
                lock();
            else
                unlock();
        }
#if DEBUG      
        Serial.println("Found a card!");
        Serial.print("UID Length: ");
        Serial.print(uidLength, DEC);
        Serial.println(" bytes");
        Serial.print("UID Value: ");
        for (uint8_t i=0; i < uidLength; i++) 
        {
            Serial.print(" 0x");Serial.print(uid[i], HEX); 
        }
        Serial.println("");
#endif    
        // Wait 1 second before continuing
        delay(1000);
    }
    else
    {
#if DEBUG        
        // PN532 probably timed out waiting for a card
        Serial.println("Timed out waiting for a card");
#endif        
    }
  
}
2 Likes

So very cool! Love to see how people are using these new cards! :smiley:

When the time is right, all the Nomos services are in place to authenticate member cards and we can easily expand on those to suit project needs!

Is there a demo to look at? I guess itā€™s json? Wondering if thereā€™s something light enough to put into an Arduino? i guess it needs https? Something simple that gives a different http error code for valid, invalid, expired cards perhaps? Ok, Iā€™ll look at the nomos source.

Thereā€™s a little bit of documentation in the API section if you log into
Nomos.

Garth also implemented some stuff on the front doorā€™s github:
https://github.com/vhs/intercom-access

I think a single central reader which pops open the correct locker door based on scanned card would be very cool.

Currently all of the end points are JSON via HTTP. It is not difficult to provide any variety of endpoints over HTTP, restful, simple response codes or formats, etc. If we want to go with any other sort of protocol aside from HTTP that is also possible but HTTP is the preferred protocol with JSON endpoints being the ideal.

Documentation is limited but as Jarrett pointed out, intercom-access shows how the pin auth works. The method is nearly identical for RFID. There is also a tab in Nomos itself that describes the basics about creating an API key and getting started with testing.

If creating an API under your user account, youā€™ll be limited to grant only the permissions you have access to with your account. Some of the Auth methods require an administrative role to add RFID/Authentication permissions to an API key. Iā€™d be more than happy to issue you an API key with the appropriate permissions as necessarily. We are still working on improving the workflow/UI to make this more readily available in a self-service manner.

See Nomos also review that Usage tab, itā€™ll provide a brief run down on how to authenticate requests with API keys and how to get a JSON response that describes all of the services currently available on our system (http://membership.vanhack.ca/services/web/help).

If youā€™re interested in looking at the code, review the service contracts to see which services we provide nomos/app/contracts at master Ā· vhs/nomos Ā· GitHub

nomos/app/services at master Ā· vhs/nomos Ā· GitHub - these are the service implementations if you need to know how they work.

nomos/app/endpoints/web at master Ā· vhs/nomos Ā· GitHub describes which endpoints are registered. As of right now they are all of type JsonEndpoint

@lathlo, can you give me an example of what Iā€™m permitted to do. I get Access denied for the things Iā€™ve tried. Thanks. I know Iā€™m posting my key here, have killed it.

image

tkeddie@tkeddie-ubuntuvm:~/tmp$ cat api.py 
import json, requests

# /services/web/UserService1.svc/GetUsers
url = "http://membership.vanhack.ca/services/web/MemberCardService1.svc/ValidateGenuineCard?key=0x040263ea504981"

headers = {'X-Api-Key': '7b103e2405c08d93e6cb146ce4d65ca6c8e47a6886df460aa68cd8c10a5a38c7'}

resp = requests.get(url=url, headers=headers)

print(resp.text)

IMembershipCardService.ValidateGenuineCard($key) is a simple service with limited permission requirements (only need to be a valid user) that you can use to validate if an RFID key is a valid and genuine VHS membership card.

http://membership.vanhack.ca/services/web/MembershipCardService.svc/ValidateGenuineCard?key=value

There arenā€™t many extra services for card services yet that will give you any real details about users or anything other than the auth services. We are kinda looking for feedback from what people want to do and then the services can be added.

If you look at nomos/app/contracts at master Ā· vhs/nomos Ā· GitHub the @permission lines will give you an idea of the requirements for certain service calls.

So interesting services for an individual user might be:

http://membership.vanhack.ca/services/web/UserService1.svc/GetStanding?userid=

This will return true/false depending on your current payment standing.

http://membership.vanhack.ca/services/web/UserService1.svc/GetUser?userid=

This will return a bunch of details about your current user profile data.

Our web UI is a pure javascript client that uses the webservices for all of the data that is displayed, so it serves as a great example of what you can do. If you use Chrome, hit Ctrl+J to open the console, click the network tab - with this open while you login to Nomos and browse around youā€™ll be able to see the service calls it makes and the responses.

To get your user id for the currently logged in user:

http://membership.vanhack.ca/services/web/AuthService1.svc/CurrentUser

This will give you a response like:

{ā€œidā€:ā€œ42ā€,ā€œpermissionsā€:[ā€œdoorā€,ā€œvettedā€,ā€œdoorā€,ā€œlaserā€,ā€œadministratorā€,ā€œuserā€]}

That is your user ID and which permissions you have available.

If you generated an API key, make sure you add permissions to that key before you try to use it.

@TomKeddie looks like I mustā€™ve broke some API key permissions at some pointā€¦ you should have an inherit option in that list of permissions for the key that would give the ā€˜userā€™ permission. ā€˜userā€™ is required for many of those servicesā€¦ at this stage itā€™s basically only allowed to call services that only require ā€˜authenticatedā€™ which is very few. Iā€™ve at least changed ValidateGenuineCard to only require ā€˜authenticatedā€™

Thanks, I see the difference, much appreciated. Iā€™ll try to put together an Arduino example so we can start sticking these things everywhereā€¦ I think this will work fine with the limited ram.

Here is a working python example.

import json, requests
url = "http://membership.vanhack.ca/services/web/MemberCardService1.svc/ValidateGenuineCard?key=04:02:63:ea:50:49:81"
headers = {'X-Api-Key': 'your key goes here'}
resp = requests.get(url=url, headers=headers)
print(resp.text)
1 Like

@TomKeddie Iā€™ve fixed the inherit option in the API keys section. That would be the only permission you need to add to get everything in the list plus ā€˜userā€™. The intention is to have granular permissions for service type categories so there is more control but at this time those donā€™t yet exist. They are easily added as we sort of discover our needs.

For now, setting the API key to Inherit will give you the important ā€˜userā€™ permission (plus everything in that list) so you can run any service you would otherwise be able to as if you logged in.

Note that this is kinda dangerous because that API key could change passwords, see and change pin access codes, etc so make sure you donā€™t leak your API keys.

In the future Iā€™ll add more granular permissions so that this isnā€™t such a big deal. Itā€™s easier from an admin perspective because we designed that sorta first where any VHS type appliance would have a dedicated API key for it.

The idea behind the member created API keys are so that people could distribute apps or whatever and people can put in their own API key without having to worry about entering in their direct credentials. We also have oauth for this for more high level type apps you may use.

All that said, if you find some services you think make sense to have specific privileges defined I will gladly add them.

@TomKeddie I also noticed in one of your above snippets that you provided a rfid key in a hex code format such as 0x 0402ā€¦ we are using a specific string format for the member cards such as:

04:02:63:ea:0f:0f:0f

At this time I believe they are also case sensitive and should be lower case for the alphabet characters.

I have logged an issue in github to track this enhancement: Support different formats for RFID member card keys Ā· Issue #157 Ā· vhs/nomos Ā· GitHub

Thanks, lower case is fine with me.

Itā€™s a fine line between privacy and features. Itā€™s almost like we want a register of applications that people can permit some of their data to be exposed to. Iā€™d like to be able to get names and emails from rfid keys but it needs to be opt in somehow.

1 Like