RFID Candy Box + Twitter



Christmas is coming. The pre-christmas season has started. Sweets everywhere. It doesn't matter, if it's classic chocolate, marzipan potatoes, baked goods or gingerbread: Everything, everywhere! Everything is within easy reach, waiting to be eagerly devoured.
Every year i swear myself, to not eat this much. But every time i lose. Every time I lie on the couch, nearly sick, swearing to do better next year. Not this time. This year i will build some prevention:
A crate, which I have to open with a RFID Token, just three times a day. Haha! Take this! Future me!

Some weeks ago I started playing around with a RFID module, I bought because it sounds interesting. I just wanted to find out, how they work, how I can talk to them and how I read and write data from/to cards. But I couldn't find a good use case for it. My landlord wouldn't like it, if I start to manipulate, I mean: refactor our door bell. Peasant!
Since a few weeks I have to drive some long distances to work twice a week. So I have a lot of time to think about stuff. So I had the idea of building a candy box, which just opens with a RFID token.
After some sketches, I decided to hide the electronics below a second bottom. Between the surfaces I will hide an arduino, which will send the requests to a controlserver. The microcontroller will spin a servo motor to the right, so I'm able to open the lid. If the lid gets closed again, the controller will notice by reading a reedsensor and will lock it again.

I used the following components:

Hint: The RFID module works with 3.3V only. 5V will destroy it.

Connect the components like below:



Add 220 Ohm resistors to the LEDs and a pullUp-circuit to the reed sensor (4.7kOhm) onto D2. Connect the red Led to D4, green to D3.
The servo gets extra 5v and its signal at D1.
Connect the RFID module like written in the following table:

Wemos D1RC522
3.3V3.3V
GGND
D0RST
D5SCK
D6MISO
D7MOSI
D8SDA




After some soldering, the finished board should look like something like this:


To hold every piece in place, I printed some parts myself. If you want to have a look or even download them, you can do so on Thingiverse.com


The servo has to be put into the fitting model and needs two screws on each side. Slide the RFID module between it's printed model and the wall of the crate. Add some screws, so that the model holds the module at the wall.
Just mount the reed sensor with two screws where ever you want. It just has to work, when the lid gets closed. I mounted the servo and the sensor in the same corner, to have everything in the same neighbourhood:

As you see, i have added a printed piece to the servo. This part got mounted with some glue and a screw, so that it blocks the lid, if the crate is locked.
The counterpart consists out of three pieces: Glue the groundplate to the middlepiece with some acetone. The third part is mounted with a screw, some washers and a nut. I just choose this solution to adjust the angle and distance. It works great. Thanks brain! Just mount the part to the lid.


All wires end up between booth wooden base plates and get connected to the soldered board:
To mount the DC jack onto the box, I added an adapter plate. Just put it onto the jack and tighten the nut. Screw the plate to the case. You should solder the wires before, otherwise it could get a little bit tight. Add the MicroUSBJack and we finished the hardware parts.


Ouh. The software is still missing.

Arduino Code

/* BunteKiste Source.
Some code to handle a crate full of sweets, to open and close it via RFID tokens
by Christian Figge - info ät flazer punked net (flazer.com)
*/

#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Servo.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>

ESP8266WiFiMulti WiFiMulti;

//RFID SPI
#define RST_PIN  D0 
#define SS_PIN   D8

//SERVO
#define SRV_PIN  D1

//REED SWITCH
#define REED_SW  D2

//STATUS LEDS
#define LED_RED  D4
#define LED_GRN  D3

MFRC522 mfrc522(SS_PIN, RST_PIN);

boolean doorState = false;
boolean isOpen = false;
int loopWaitDoorCheck = 0;
int blameCount = 0;

Servo servo;

/**
 * Initialize.
 */
void setup() {
    Serial.begin(57600);
    while (!Serial); 
    SPI.begin();
    mfrc522.PCD_Init();

    WiFiMulti.addAP("YOUR_AP_SSID", "YOUR_AP_PASSWORD");

    servo.attach(SRV_PIN);
    pinMode(REED_SW, INPUT);
    pinMode(LED_RED, OUTPUT);
    pinMode(LED_GRN, OUTPUT);

    //Just light both LEDs for 2 secs
    displayWelcome();
    displayBlame();
    delay(2000);
    ledsOut();

    handleServo(false);
    doorState = getCurrentDoorState();
}

/**
 * Main loop.
 */
void loop() {
    delay(100);
    if(waitForDoorStatusChange()) {
        delay(2000);
        if(!doorState) {
            if(isOpen) {
                handleServo(false);
                isOpen = false;
                ledsOut();
            }
        }
    }

    if(blameCount > 0) {
        blameCounter();
    }

    // Look for new cards
    if (!mfrc522.PICC_IsNewCardPresent())
        return;

    // Select one of the cards
    if (!mfrc522.PICC_ReadCardSerial())
        return;

    MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);

    // Check for compatibility
    if (piccType != MFRC522::PICC_TYPE_MIFARE_MINI
        &&  piccType != MFRC522::PICC_TYPE_MIFARE_1K
        &&  piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
        Serial.println(F("No MIFARE Classic card."));
        return;
    }

    String uid = getID();
    Serial.print("UID: "); Serial.print(uid);
    Serial.println("");

    if(uid != "") {
        if(check(uid)) {
            Serial.println("Simsalabim");
            handleServo(true);
            isOpen = true;
            displayWelcome();
        }else{
            blameCount = 1;
            displayBlame();
        }
    }

    mfrc522.PICC_HaltA();
    mfrc522.PCD_StopCrypto1();
}

/**
 * handles time for showing blame led
 */
void blameCounter() {
    blameCount++;
    if(blameCount > 10) {
      ledsOut();
      blameCount = 0;
    }
}

/**
 * fires green led
 */
void displayWelcome() {
    digitalWrite(LED_GRN, 1);
}

/**
 * fires red led
 */
void displayBlame() {
    digitalWrite(LED_RED, 1);
}

/**
 * just blackout both leds
 */
void ledsOut() {
    digitalWrite(LED_RED, 0);
    digitalWrite(LED_GRN, 0);
}

/**
 * returns current lid's state
 */
boolean getCurrentDoorState() {
    boolean status = false;
    if(digitalRead(REED_SW) < 1) {
        status = true;
    }
  return status;
}

/**
 * checks if status of lid changes
 **/
boolean waitForDoorStatusChange() {
    if(doorState != getCurrentDoorState()) {
        doorState = getCurrentDoorState();
        if(!doorState) {
            Serial.println("CLOSED!!!!");
        }else{
            Serial.println("OPENED!!!!");
        }
        return true;
    }
    return false;
}

/**
 *  Sends request to server
 */
boolean check(String uid) {
    boolean result = false;
    if(WiFiMulti.run() != WL_CONNECTED) {
        Serial.println("NOT CONNECTED!");
        return false;
    }
    HTTPClient http;
    Serial.println("[HTTP] begin...");
    http.begin("http:///PATH_TO_YOUR_CONTROLLSERVER/api/crate/" + uid); //HTTP
    Serial.println("[HTTP] GET...");
    int httpCode = http.GET();
    if(httpCode > 0) {
        Serial.printf("[HTTP] GET... code: %d\n", httpCode);
        Serial.println("");
        if(httpCode == HTTP_CODE_OK) {
            String payload = http.getString();
            Serial.println(payload);
            if(payload == "granted") {
                result = true;
            }
        }
    }else{
        Serial.printf("[HTTP] GET... failed, error: %s\n", http.errorToString(httpCode).c_str());
    }
    http.end();
    return result;
}

/**
 * Handles Servo to open/close the crate
 */
void handleServo(boolean direction) {
    int pos;
    if(direction) {
        for(pos = 0; pos <= 120; pos += 1) {
            servo.write(pos);
            delay(1); 
        }
    }else if(!direction) {
        for(pos = 180; pos>=0; pos-=1) {                                
            servo.write(pos);
            delay(1); 
        }
    }
}

/**
 * Get Uid and transform to uppercase
 */
String getID(){
    String code ="";
    for (byte i = 0; i < mfrc522.uid.size; i++) {
        code += String(mfrc522.uid.uidByte[i], HEX) + ":";
    }

    code.remove(code.length()-1);
    code.toUpperCase();
    return code;
}

/**
 * Helper routine to dump a byte array as hex values to Serial.
 */
void dump_byte_array(byte *buffer, byte bufferSize) {
    for (byte i = 0; i < bufferSize; i++) {
        Serial.print(buffer[i] < 0x10 ? " 0" : " ");
        Serial.print(buffer[i], HEX);
    }
}

Just some information about the code:

The Arduino sends, after it found a card, a request via WIFI to an API (website). The API takes 2 parameters: The type of lock (crate) and the UID of the token/card. Now the server is checking, if it is a known token and how much the crate got opened today. If the limit is reached, or the token is unknown, the API will answer "denied". If everything is okay, it will send an "granted".


The code is no masterpiece, this is because of:

  • My absolute inability to write clean code
  • My failure to understand C
  • The underestimation of time and then I had to hurry

You have to change the following parts:

  • YOUR_AP_SSID - Name of your WIFI
  • YOUR_AP_PASSWORD - WIFI's password
  • PATH_TO_YOUR_CONTROLLSERVER - API's URL

The code will not work. You have to install the RFID library first. You'll find it on Github: https://github.com/miguelbalboa/rfid. Just download the "Master", unzip and copy it to Arduino's "libraries"-folder. Remove the "-master" from the folder's name.
To programm the board, you also have to install the driver, because the WeMos D1 doesn't use the standard FTDI chip. Instead the used chip is an "CH340G". I found the driver for windows here: http://www.arduined.eu/tag/ch340g/ for Mac at Björns Blog: https://blog.sengotta.net/arduino-nano-wird-nicht-erkannt-was-tun/.

But we need some more stuff: We have to add the new board to the Arduino IDE as well. So just add the following URL to the "Additional Board Manager URLs" in settings:
http://arduino.esp8266.com/stable/package_esp8266com_index.json


If the URL shouldn't work in future, you should take a look at it's repo: https://github.com/esp8266/Arduino
Now you have to download the package via Arduino's board manager. You are able to do this, by clicking on "Tools -> Board -> Boardmanager". Search for ESP8266 and click on "install". This will take a while. Afterwards you can choose a lot more boards:


Yeah! Now we are able to compile and upload the code.

Unfortunately I can not publish the code of the API, because I wrote big parts of the backend at work (Jeah, I have a normal job). We needed a cool ACP and so I could mix freetime and work. But if you want to build your own: I used a lot of OpenSource. Here are some information and screenshot about the thing i build myself:

The webserver is an Nginx with a PHP-extension on a Raspberry Pi. I'm using a MariaDB as database. And because I'm tired of writing the same things again and again, I decided to work with Laravel 5.3 as a framework.
The theme is .a free one, called "Matrix". This got a little bit modified with Bootstrap and some Javascript (JQuery). After some successfull modifications it looks like this:


Okay. It's not the nicest one, but it's doing its job.

And because, you should not always just use OpenSource, without giving something back, we have written another Twitter-API. You are nearly able to use this API while you are asleep. If you are interested, you can download or develop it here: https://github.com/FrozenDonkey/twitter-api-php. This code gets used in this project aswell. The crate is tweeting random texts, when somebody tries to open the lid.

How you implement the code?
Really Easy:

  • Download it
  • Unzip it
  • Create a twitter dev account (https://dev.twitter.com)
  • Create an app -> https://apps.twitter.com
  • Replace the following block in index.php:
    $twitter = new TwitterAPI(
    "YOUR_OAUTH_ACCESS_TOKEN",
    "YOUR_OAUTH_ACCESS_TOKEN_SECRET",
    "YOUR_CONSUMER_KEY",
    "YOUR_CONSUMER_SECRET"
    );

    Now you can tweet just with the following line:

    $res = $twitter->tweet("THIS IS YOUR TWEET");

    If you want to add an image:

    $res = $twitter->tweetImage('THIS IS A TWEET WITH AN IMAGE', 'path/to/image.jpg');

    BAM!!!!!


    After Bracka added a logo, the box looks like this:



    If you want to have a look at the crate's twitter account, just search for @DieBunteKiste. Or click on the link.

    And here you see a video of how I built the whole thing: (german only)

30 Comments

Jonas wrote on 11-30-2016 00:55:
Danke für die Anleitung Versuch grad die Box nachzubauen, da kommt der Blogeintrag gerade richtig dass das Projekt bis Weihnachten fertig wird. :)
Tjard wrote on 11-30-2016 13:50:
Welchen API code
flazer wrote on 11-30-2016 13:54:
Was genau meinst du? TwitterAPI, oder die API auf dem Webserver?
Tjard wrote on 11-30-2016 14:52:
du hast geschrieben das die api während der aerbeit entstanden seie
Tjard wrote on 11-30-2016 14:59:
hab's schon kapiert verstehe nur php nicht so gut:(((( muss mich um meine Künste in dieser Sprache noch kümmern hab ja aber noch ein paar Schuljahre vor mir ;)
0 wrote on 12-01-2016 05:59:
Ich wollte Mal Fragen wie ich es ohne Internet mache (also nur das man nur 3 Mal am Tag was nehmen kann).
Tjard wrote on 12-01-2016 07:28:
Nein doch statt dem WLAN ne Variable die kleiner als drei sein muss und bei jedem Öffnen um 1 erhöht wird
MaX wrote on 12-01-2016 21:02:
Wie programmiert man dann das es sich täglich resetet?
Man könnte nach den drei versuchen ja einfach eine enorm große Waartezeit eingeben, aber so macht das jakeinen Spaß
Tjard wrote on 12-02-2016 07:31:
Man kann mit PHP auch ne Zeit Anzeige programmieren und daraus die Werte ziehen
Tjard wrote on 12-02-2016 07:40:
Was ist mit your oauth accesse Token gemeint?
flazer wrote on 12-02-2016 08:34:
Die Begrenzung auf 3x am Tag regelt die Datenbank der API. Diese schaut nach, wie oft am Tag die Kiste von XY bereits geöffnet wurde und reagiert dementsprechend. Das passiert also nicht in der Box selbst, sondern auf dem Raspberry.
flazer wrote on 12-02-2016 08:35:
Hey Tjard,
schau mal hier vorbei: https://dev.twitter.com/oauth/overview Die Tokens bekommst du, wenn du bei Twitter eine App anlegst.
Peter wrote on 12-04-2016 09:55:
wie heisst das CAD Zeichenprogramm?
NoneType(null) wrote on 12-04-2016 13:30:
Sorry, wenn ich was blöd frage, aber wie heißt diese Pappe wo du die Hardware drauf lötest?
flazer wrote on 12-04-2016 14:27:
Das "Programm" ist Tinkercad.com und die "Pappe" nennt sich Lochraster:. Guck mal hier: https://www.reichelt.de/Lochraster-Loetpunkte/2/index.html?ACTION=2&GROUPID=7785 G
DickesKind wrote on 12-04-2016 15:18:
Servus Chris,
ich würde gern für mein Werkstatt-Verein ein RFID-System aufbauen bei welchem man über die Karte steuern kann wer zu welchen Maschinen Zugriff hat und wer nicht. D.h. das gleiche Problem wie du hier lösen. Den Arduino-Teil kann ich verstehen aber die Webinterface Sache ist mir total fremd. Hast du hier paar Stickpunkte die ich in Google hauen kann damit ich mir sowas auch machen kann oder ist das zu hoch für einen Maschbauer?
Gruß Paul
Jascha wrote on 12-10-2016 15:24:
Moin,

hab die Hardware auf dem Steckbrett schon aufgebaut und das Sketch auch erfolgreich geladen. Irgendwie zuckt der Servo permanent und wird ziemlich heiß...

Kannst du vielleicht den Teil auf dem Arduino nochmal genauer beschreiben? Verstehe da nur Bahnhof :D
Jonas wrote on 12-13-2016 23:33:
Moin ich habe deine Box nachgebaut und wollte gerade mal den sketch kompilieren jedoch bekomme ich nun folgende Meldung :
warning: espcomm_sync failed
error: espcomm_open failed
error: espcomm_upload_mem failed
error: espcomm_upload_mem failed
Kann mir dabei jemand weiter helfen?
Max wrote on 12-26-2016 10:21:
Moin,

habe heute begeistert begonnen das Projekt umzusetzen - leider scheitere ich beim Kompilieren. Ich erhalte folgende Fehlermeldung:

WARNUNG: Bibliothek rfid behauptet auf [avr Architektur(en) ausgeführt werden zu können und ist möglicherweise inkompatibel mit Ihrem derzeitigen Board, welches auf STM32F1 Architektur(en) ausgeführt wird.
C:\Users\Max\Desktop\SussigkeitenBox\SussigkeitenBox.ino: In function 'void setup()':

SussigkeitenBox:56: error: 'displayWelcome' was not declared in this scope

displayWelcome();

^
[...]

Meine Einstellungen für das Board: https://img3.picload.org/image/raowlggo/einstellungen.jpg

Kannst du mir einen Hinweis geben woran das liegen kann?

Danke vorab und Gruß aus Hannover,

Max
Robiotik wrote on 12-28-2016 00:20:
Voll Coole Idee Sowas zu Bauen, wie währe es wenn man das Als Getränke Automat Verwendet?
flazer wrote on 12-29-2016 07:15:
Hey Robiotik:
Klingt cool. Aber ich wüsste nicht, wohin mit dem Ding. :D
flazer wrote on 12-29-2016 07:16:
Hey Max:
Das ist "normal". Einfach ignorieren. Bei mir läuft's trotzdem.
Max wrote on 01-04-2017 00:21:
Hey Christian,

hoffe du bist gut ins neue Jahr gekommen!

Danke für den Tipp, ich habe es jetzt nochmal ausgeführt und nun funktioniert es (auch ohne Anzeige der anderen Fehler...) - keine Ahnung was da schief gelaufen ist.

Ich würde gerne die Box umgehen und das ganze in meinen Schrank einbauen - aber leider ist der RFID- Empfänger zu schwach durch das 1 cm dicke Holz zu kommen :(

Ist es möglich über den Anschluss dieses Moduls den Empfangsbereich zu vergrößern?: https://picload.org/image/rappwawl/s-l500.jpg

Danke vorab und eine schöne Woche!

LG

Max
Arduino-Freak wrote on 01-21-2017 07:09:
Hi Flazer,

bin ein großer Fan von deinen Projekten.
Habe allerdings ein paar Fragen:

1.Was für eine Krimpzange benutzt du und welche "Steckerhülsen" aus Plastik mit welchen Aderhülsen?

2.Habe mir ein Termometer mit einem DHT-11 und einem Nano gebaut und wollte fragen ob du/sie auch Mal was in diese Richtung machen könntest/können.

Freue mich schon auf die Antworten und Danke im Voraus.

LG Arduino-Freak
Yugee wrote on 02-09-2017 15:07:
Ich hätte da auch mal schnell eine Frage. Wie nennt sich diese Testplatine, wo man erstmal Kreisläufe ausprobieren kann. Mfg
Arduino-Freak wrote on 02-10-2017 06:11:
Das nennt sich Steckplatine...
sebi wrote on 03-28-2017 14:05:
wie öffnet man die Einstellungen wo man die Bordverwaltung URLs eingeben kann
sebi wrote on 03-28-2017 20:13:
geht das ganze auch auf Windows weil jedes mal wenn ich bei Bordverwaltung esp 8266 eingebe findet der nichts.
bitte um schnelle antwort

danke schon im voraus

mfg sebastian
sebi wrote on 03-30-2017 17:15:
Braucht mann für das ganze ein Resperry pie
Lennart wrote on 04-10-2017 23:02:
Hi

Wo kaufst du deine Litze? Ein Link würde mich freuen.

Wenn es keine zu großen Umstände macht, würde ich mich über eine Antwort per E-Mail freuen: lennih14@gmail.com
Name or Nickname:
Comment: