electrónica

Monedero electrónico con PHP y RFID

En este post te mostraré cómo implementar un pequeño monedero electrónico, tarjetas de saldo o tarjetas de débito usando PHP y etiquetas RFID.

Como bien sabes, las etiquetas RFID están presentes en varios componentes, entre ellos una tarjeta. Así que podemos usar esa tarjeta para guardar el saldo de un usuario.

Lo que te mostraré será cómo guardar y leer el dinero / saldo en las tarjetas, mostrarlo en una LCD para que el usuario lo pueda ver, y además implementar la parte del servidor con PHP para poder recargar y comprar cosas.

Básicamente será un monedero electrónico o una tarjeta con la que el usuario podrá comprar y recargar. A partir de aquí puedes mejorar el código para adaptarlo a tus necesidades.

Lector RFID

Lo que yo usaré es la tarjeta NodeMCU ESP8266, el lector MFRC522 RFID-RC522 y una LCD de 16 x 2 usando un módulo I2C. La comunicación con el servidor la haremos a través del WiFi que ya viene integrado en la ESP8266.

El circuito de conexión queda como se ve a continuación. Obviamente tú puedes reemplazar componentes, mejorar conexiones u optimizar el modelo si es que sabes lo que haces.

Circuito para monedero electrónico RFID y ESP8266

En mi caso lo he montado así:

Lector RFID con LCD para implementar tarjeta de débito y guardar saldo o dinero

Lecturas recomendadas

Aquí voy a saltarme algunos pasos básicos, así que te recomiendo ir a los posts que citaré a continuación en caso de que tengas dudas adicionales o quieras aprender a fondo. También me voy a saltar lo de las librerías, pues eso ya está en sus respectivos posts.

Yo usaré Visual Studio Code para programar la tarjeta. Vamos a hacer peticiones HTTP a PHP, además de leer y escribir datos en las tarjetas RFID. También vamos a imprimir en la LCD, para lo cual necesitamos conocer la dirección I2C.

Explicación de la arquitectura

LCD preparada para leer RFID

Tan pronto se lea una tarjeta, vamos a mostrar el saldo del usuario en la LCD. Si la tarjeta es nueva, el saldo será 0. Además de mostrar el dinero de la tarjeta vamos a comenzar a hacer peticiones al servidor con PHP y Apache para esperar dos cosas:

  • Recargar saldo: el servidor le ordenará a la tarjeta que recargue saldo al usuario
  • Descontar saldo: el servidor va a indicar que la tarjeta descuente saldo si es que eso es posible

Mientras no haya órdenes por parte del servidor, la ESP8266 va a estar consultando cada 5 segundos. Tan pronto reciba una orden, la va a ejecutar e indicar los resultados a través de la LCD.

Para realizar una compra o una recarga he escrito el frontend en JavaScript. Lo que se hace es simplemente indicarle al servidor PHP la orden que le tiene que dar a la tarjeta, y después de eso igualmente comenzamos a realizar peticiones cada 2 segundos para ver lo que la tarjeta ha respondido.

Código para la tarjeta ESP8266

Aquí dejo el código completo de la tarjeta. No olvides cambiar la dirección del I2C, así como las credenciales para conectar a la red WiFi y la dirección de tu servidor, que puede ser de la red local o un servidor en internet (para esto último tendrás que configurar el certificado en caso de usar https).

Por cierto, fíjate en las funciones de guardarSaldo y obtenerSaldo, pues son muy importantes y se encargan de leer o escribir los bloques de la tarjeta RFID. Si te quedan dudas recuerda que anteriormente te dejé los posts en donde explico eso más a fondo.

/*
  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
            | IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include <LiquidCrystal_I2C.h>

#define LONGITUD_BYTES 18
#define LONGITUD_BYTES_ESCRITURA 16
#define SEPARADOR_ORDENES_SERVIDOR ";"
#define ORDEN_RECARGAR "r"
#define ORDEN_DESCONTAR "d"
/*
Pines para conectar el lector
*/#define RST_PIN D3
#define SS_PIN D4

// Credenciales para conectar a la red

const char *ssid = "Aquí va el nombre de tu red";
const char *password = "Aquí va la contraseña de tu red";
const String DIRECCION_SERVIDOR = "http://192.168.1.82/rfid_saldo";
// Conexión a WiFi
ESP8266WiFiMulti wifiMulti;
// La LCD
LiquidCrystal_I2C lcd(0x3f, 16, 2);

// El lector
MFRC522 lector(SS_PIN, RST_PIN);
MFRC522::MIFARE_Key clave;
int tarjetaLeidaDesdeLoop = 0;
double saldoGlobal;

void obtenerSaldo(double *saldo)
{

  if (!lector.PICC_IsNewCardPresent())
  {
    *saldo = -1;
    Serial.println("No hay nueva tarjeta presente");
    return;
  }
  if (!lector.PICC_ReadCardSerial())
  {
    *saldo = -1;
    Serial.println("Error leyendo serial");
    return;
  }

  byte bloque, longitud, buferLectura[LONGITUD_BYTES];
  MFRC522::StatusCode estado;
  bloque = 1;
  estado = lector.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloque, &clave, &(lector.uid));
  if (estado != MFRC522::STATUS_OK)
  {
    Serial.println("Error autenticando");
    Serial.println(lector.GetStatusCodeName(estado));
    *saldo = -1;
    return;
  }
  longitud = LONGITUD_BYTES;
  estado = lector.MIFARE_Read(bloque, buferLectura, &longitud);
  if (estado != MFRC522::STATUS_OK)
  {
    Serial.println("Error leyendo bloque");
    Serial.println(lector.GetStatusCodeName(estado));
    *saldo = -1;
    return;
  }
  char mensaje[LONGITUD_BYTES] = "";

  for (uint8_t i = 0; i < LONGITUD_BYTES - 2; i++)
  {
    mensaje[i] = buferLectura[i];
  }
  double saldoLeido = atof(mensaje);
  Serial.println("Se lee el saldo");
  Serial.println(saldoLeido);
  *saldo = saldoLeido;
  lector.PICC_IsNewCardPresent();
}

void guardarSaldo(double saldo)
{

  if (!lector.PICC_IsNewCardPresent())
  {
    Serial.println("No hay nueva tarjeta presente al guardar saldo");
    return;
  }
  if (!lector.PICC_ReadCardSerial())
  {
    Serial.println("Error leyendo serial");
    return;
  }
  Serial.println("Se guarda el saldo");
  Serial.println(saldo);

  byte bloque, buferEscritura[LONGITUD_BYTES_ESCRITURA];
  // Convertir saldo a cadena
  char cadena[LONGITUD_BYTES_ESCRITURA] = "";
  snprintf(cadena, LONGITUD_BYTES_ESCRITURA, "%.2lf", saldo);
  // Copiar cadena al búfer
  for (uint8_t i = 0; i < LONGITUD_BYTES_ESCRITURA; i++)
  {
    buferEscritura[i] = cadena[i];
  }
  MFRC522::StatusCode estado;
  bloque = 1;
  estado = lector.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, bloque, &clave, &(lector.uid));
  if (estado != MFRC522::STATUS_OK)
  {
    Serial.println("Error autenticando");
    Serial.println(lector.GetStatusCodeName(estado));
    return;
  }
  estado = lector.MIFARE_Write(bloque, buferEscritura, LONGITUD_BYTES_ESCRITURA);
  if (estado != MFRC522::STATUS_OK)
  {
    Serial.println("Error escribiendo bloque");
    Serial.println(lector.GetStatusCodeName(estado));
    return;
  }
  // Ya pueden retirar la tarjeta

  lector.PICC_HaltA();
  lector.PCD_StopCrypto1();
  tarjetaLeidaDesdeLoop = 0;

  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("SALDO:");
  lcd.setCursor(6, 0);
  lcd.print(saldo);
  lcd.setCursor(0, 1);
  lcd.print("RETIRE LA TARJETA");
}

void setup()
{
  // Iniciar comunicación Serial. Esto es útil solo para depurar, después se puede remover
  Serial.begin(9600);
  while (!Serial)
  {
    // Esperar serial. Nota: la tarjeta NO HARÁ NADA hasta que haya comunicación Serial (es decir, que el monitor serial sea abierto)
    // si tú no quieres esto, simplemente elimina todas las llamadas a Serial
  }
  // Iniciar LCD
  lcd.begin(16, 2); // <----------- En algunas versiones de NodeMCU (v2) debe llamarse antes de todo, antes de SPI.begin(); y antes de lector.PCD_Init();
  lcd.init();       // Encender la LCD
  lcd.backlight();

  // Iniciar lector
  SPI.begin();
  lector.PCD_Init();
  // Esperar algunos segundos
  delay(4);
  // Preparar la clave para leer las tarjetas RFID
  for (byte i = 0; i < 6; i++)
  {
    clave.keyByte[i] = 0xFF;
  }

  // Aquí puedes agregar varias redes. La tarjeta se conectará a la más cercana
  wifiMulti.addAP(ssid, password);
  // wifiMulti.addAP("Otra red", "Contraseña");

  // Esperar conexión WiFi. La tarjeta NO HARÁ NADA si no hay conexión WiFi
  Serial.print("Conectando al WiFi...");
  while (wifiMulti.run() != WL_CONNECTED)
  {
    delay(250);
    Serial.print(".");
  }
  lcd.clear();
  lcd.print("TODO OK");
  lcd.setCursor(0, 1);
  lcd.print("COLOCAR RFID");
  Serial.println("Todo OK");
}

// Loop infinito...
void loop()
{

  if (WiFi.status() != WL_CONNECTED)
  {
    // Si no hay WiFi, no hacemos nada
    Serial.println("No hay WiFi");
    lcd.clear();
    lcd.print("NO HAY");
    lcd.setCursor(0, 1);
    lcd.print("WIFI");
    return;
  }
  // Si no han colocado la tarjeta, vamos a ver si hay alguna presente
  if (!tarjetaLeidaDesdeLoop)
  {
    // Si no hay ninguna, nos detenemos
    if (!lector.PICC_IsNewCardPresent())
    {
      return;
    }

    // Si no se puede elegir la tarjeta, nos detenemos
    if (!lector.PICC_ReadCardSerial())
    {
      return;
    }
    Serial.println("Tarjeta leída");
    // hasta aquí ya hemos leído la tarjeta, así que marcamos la bandera en true (1)
    tarjetaLeidaDesdeLoop = 1;
    // Curiosamente esta línea hace que las siguientes llamadas a PICC_IsNewCardPresent devuelvan true
    lector.PICC_IsNewCardPresent();
    // Obtenemos el saldo de la tarjeta actualmente colocada en el lector
    obtenerSaldo(&saldoGlobal);
    // Lo imprimimos en la LCD
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("SALDO:");
    lcd.setCursor(6, 0);
    lcd.print(saldoGlobal);
    lcd.setCursor(0, 1);
    lcd.print("NO RETIRE RFID");
    lector.PCD_StopCrypto1();
  }
  else
  {
    Serial.println("Tarjeta en lector. Verificando con servidor");
    // Significa que la tarjeta ya está colocada en el lector, vamos a consultar el servidor para ver si hay que hacer algún cambio
    HTTPClient http;
    // Invocamos al servidor y le pasamos el saldo que hay actualmente
    String url = DIRECCION_SERVIDOR + "/index.php?saldo=" + saldoGlobal;
    http.begin(url);

    int httpCode = http.GET();
    if (httpCode > 0)
    {
      if (httpCode == HTTP_CODE_OK)
      {
        // si el servidor responde correctamente, obtenemos lo que nos haya respondido
        String payload = http.getString();
        // Leemos la respuesta en una cadena válida de C, no de la clase String
        char payloadComoCadenaValida[50] = "";
        for (uint8_t i = 0; i < payload.length(); i++)
        {
          payloadComoCadenaValida[i] = payload.charAt(i);
        }
        // El servidor nos dará una orden como "d;10" que quiere decir "descontar 10" así que debemos separar la cadena por ;
        char delimitador[] = ";";
        char *ordenExtraida = strtok(payloadComoCadenaValida, delimitador);
        char *saldoExtraido = strtok(NULL, delimitador);
        if (ordenExtraida != NULL && ordenExtraida != NULL)
        {
          // Extraemos el saldo que el servidor nos indica
          double diferenciaSaldo = atof(saldoExtraido);
          // Si se recarga, sumamos el saldo y lo guardamos
          if (strcmp(ordenExtraida, ORDEN_RECARGAR) == 0)
          {
            guardarSaldo(saldoGlobal + diferenciaSaldo);
          }
          else if (strcmp(ordenExtraida, ORDEN_DESCONTAR) == 0)
          {
            // En este caso, se descuenta el saldo
            double nuevoSaldo = saldoGlobal - diferenciaSaldo;
            // Guardar el nuevo saldo solo si el usuario tiene lo suficiente
            if (nuevoSaldo >= 0)
            {
              guardarSaldo(nuevoSaldo);
            }
            else
            {
              // Si el saldo fuera insuficiente, se indica y se guarda el mismo que había en la tarjeta. Es decir, no se hace ningún cambio
              guardarSaldo(saldoGlobal);
              lcd.clear();
              lcd.setCursor(0, 0);
              lcd.print("SALDO INSUFICIENTE");
              lcd.setCursor(0, 1);
              lcd.print("RETIRE TARJETA");
            }
          }
        }
      }
      else
      {
        Serial.println("Error: httpCode es " + http.errorToString(httpCode));
      }
    }
    else
    {
      Serial.println("Error en la solicitud" + http.errorToString(httpCode));
    }
    http.end(); //Cerramos la solicitud
    // Y hacemos esta petición cada 5 segundos
    delay(5000);
  }
}

Fíjate en que para las órdenes del servidor estoy usando el separador de ; y luego utilizo strtok para separar la orden y el saldo. Por otro lado estoy usando strcmp para comparar las cadenas.

Realizar compra con RFID y descontar saldo

Veamos un poco el lado del servidor para este monedero con PHP y la tarjeta ESP8266 que bien podría ser remplazada por un Arduino. Primero tenemos la interfaz con productos estáticos (más tarde se podrían obtener desde una base de datos):

Interfaz para comprar productos en PHP usando tarjeta RFID con saldo

Recuerda que debes acceder al lugar en donde hayas montado tu proyecto y acceder a comprar.php, en mi caso lo he montado en C:\xampp\htdocs\rfid_saldo así que la URL es localhost/rfid_saldo/comprar.php.

Cada producto tiene un data-precio para indicar el precio, así:

<!-- 
Guardamos el precio del producto en el data-precio
-->
<tbody>
    <tr>
        <td>Galletas</td>
        <td>10.5</td>
        <td>
            <button class="button is-info comprar" data-precio="10.5">Comprar</button>
        </td>
    </tr>
    <tr>
        <td>Champú</td>
        <td>5000</td>
        <td>
            <button class="button is-info comprar" data-precio="5000">Comprar</button>
        </td>
    </tr>
    <tr>
        <td>Audífonos</td>
        <td>450</td>
        <td>
            <button class="button is-info comprar" data-precio="450">Comprar</button>
        </td>
    </tr>
    <tr>
        <td>Mi nuevo producto</td>
        <td>2000</td>
        <td>
            <button class="button is-info comprar" data-precio="2000">Comprar</button>
        </td>
    </tr>
     <tr>
        <td>Mi otrooo producto</td>
        <td>123</td>
        <td>
            <button class="button is-info comprar" data-precio="123">Comprar</button>
        </td>
    </tr>


</tbody>

Luego de eso se escucha el clic del botón, se extrae el precio y se le solicita al servidor que le indique al lector descontar esa cantidad:

// Si hacen clic en un botón para comprar...
for (const $boton of $botonesComprar) {
    $boton.onclick = async () => {
        // Extraemos precio y le decimos al servidor que se ponga en modo "descontar saldo" tan pronto el lector lo "contacte"
        const precio = parseFloat($boton.dataset.precio);
        $estado.textContent = "Estableciendo conexión con servidor...";
        const respuestaRaw = await fetch("./establecer_modo_compra.php?saldo=".concat(precio));
        const respuestaJson = await respuestaRaw.json();
        if (respuestaJson) {
            $estado.textContent = "OK. Coloque la tarjeta en el lector";
        } else {

            $estado.textContent = "Error estableciendo comunicación con servidor";
        }
        // Ahora el servidor ya está en espera de que el lector "le hable". Así que consultamos cada 2 segundos si hay novedades
        const esperarLector = async () => {
            // Deshabilitamos todos los botones, pues estamos esperando
            $botonesComprar.forEach($boton => {
                $boton.disabled = true;
                if (!$boton.classList.contains("is-loading")) {
                    $boton.classList.add("is-loading");
                }
            });
            // Verificamos...
            $estado.textContent = "Verificando...";
            const respuestaRaw = await fetch("./ver_mensaje_lector.php");
            const texto = await respuestaRaw.text();
            // Si se devuelve una cadena no vacía, entonces la mostramos (la cadena puede tener un mensaje de éxito o de error)
            if (texto) {
                $estado.textContent = texto;

                $botonesComprar.forEach($boton => {
                    $boton.disabled = false;
                    $boton.classList.remove("is-loading")
                });
            } else {
                // Si el servidor indica que el lector no se ha comunicado, lo indicamos y volvemos a intentar dentro de 2 segundos
                $estado.textContent = "No se ha leído nada. Recuerde colocar la tarjeta en el lector. Intentando de nuevo...";
                setTimeout(esperarLector, 2000);
            }
        };
        esperarLector();
    };
}

Y después de eso va a estar invocando a la función esperarLector para que nos indique cuando el lector haya indicado que se ha descontado el saldo del usuario.

El código de PHP que establece el modo de compra es el siguiente:

<?php
/*

  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
            | IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/ ?>
<?php
// Le indicamos al lector lo que debe hacer. En este caso es "d;SALDO" es decir, descontar lo que haya en $saldo, misma variable que nos la darán al hacer click
// en "comprar"
include_once "constantes.php";
$saldo = $_GET["saldo"];
file_put_contents(ARCHIVO_RESPUESTA_LECTOR, ""); // Limpiar lo que haya dicho la ESP8266
file_put_contents(ARCHIVO_ORDEN_LECTOR, "d;$saldo"); // Indicarle al lector lo que tiene que hacer
// Decirle a la página de compra que la operación de cambio de operación de lector fue exitosa
echo json_encode(true);

La respuesta del lector

Después de recibir una orden, el lector va a respondernos para saber si la transacción ha sido exitosa, ya que para este proyecto de tarjetas de saldo con PHP y RFID se puede dar el caso de que el saldo sea insuficiente o alguna otra situación.

Saldo insuficiente al comprar con RFID

Para ello PHP se encarga de establecer la respuesta del lector:

<?php
/*

  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
            | IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/ ?>
<?php
// Este archivo lo va a estar consultando el lector, recordemos que nos dará su saldo
include_once "constantes.php";
// Crear archivos en caso de que no existan
if (!file_exists(ARCHIVO_ORDEN_LECTOR)) {
    touch(ARCHIVO_ORDEN_LECTOR);
}

if (!file_exists(ARCHIVO_RESPUESTA_LECTOR)) {
    touch(ARCHIVO_RESPUESTA_LECTOR);
}
$saldo = floatval($_GET["saldo"]);
// Si no le han dado una orden, simplemente imprimimos "" y el  lector no hará nada con la tarjeta
$contenido = trim(file_get_contents(ARCHIVO_ORDEN_LECTOR));
if ($contenido === "") {
    echo "";
} else {
    // En caso de que haya una orden...
    $opciones = explode(";", $contenido);
    // Extraemos la orden (descontar o recargar) y el saldo
    $accion = $opciones[0];
    $monto = floatval($opciones[1]);
    if ($accion === "d") {
        // Si la acción es descontar pero el saldo no es suficiente, lo indicamos en la página web (el lector hará la misma comprobación)
        if ($saldo - $monto < 0) {
            file_put_contents(ARCHIVO_RESPUESTA_LECTOR, "Saldo insuficiente. Retire la tarjeta");
        } else {
            file_put_contents(ARCHIVO_RESPUESTA_LECTOR, "Compra exitosa. Retire la tarjeta");
        }
    } else if ($accion == "r") {
        file_put_contents(ARCHIVO_RESPUESTA_LECTOR, "Recarga exitosa. Retire la tarjeta");
    }
    echo $contenido;
    // Y limpiamos la orden del lector
    file_put_contents(ARCHIVO_ORDEN_LECTOR, "");
}

Misma que luego es consultada al comprar o recargar:

<?php
/*

  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
            | IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/ ?>
<?php
// Leemos lo que haya dicho el lector (por ejemplo: recarga exitosa, saldo insuficiente, dependiendo de lo que haya pasado en index.php)
include_once "constantes.php";
// Creamos en caso de que no exista
if (!file_exists(ARCHIVO_RESPUESTA_LECTOR)) {
    touch(ARCHIVO_RESPUESTA_LECTOR);
}
$contenido = file_get_contents(ARCHIVO_RESPUESTA_LECTOR);
file_put_contents(ARCHIVO_RESPUESTA_LECTOR, "");
echo $contenido;

Recargar saldo

Ya vimos la operación de descontar saldo o crédito a la tarjeta con PHP y ESP8266 usando RFID, ahora vamos a ver cómo recargar saldo. El proceso es similar:

Recargar saldo al monedero con PHP y tarjetas RFID

En este caso solicitamos que se recargue saldo:

<?php
/*

  ____          _____               _ _           _       
 |  _ \        |  __ \             (_) |         | |      
 | |_) |_   _  | |__) |_ _ _ __ _____| |__  _   _| |_ ___ 
 |  _ <| | | | |  ___/ _` | '__|_  / | '_ \| | | | __/ _ \
 | |_) | |_| | | |  | (_| | |   / /| | |_) | |_| | ||  __/
 |____/ \__, | |_|   \__,_|_|  /___|_|_.__/ \__, |\__\___|
         __/ |                               __/ |        
        |___/                               |___/         
    
____________________________________
/ Si necesitas ayuda, contáctame en \
\ https://parzibyte.me               /
 ------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
Creado por Parzibyte (https://parzibyte.me).
------------------------------------------------------------------------------------------------
            | IMPORTANTE |
Si vas a borrar este encabezado, considera:
Seguirme: https://parzibyte.me/blog/sigueme/
Y compartir mi blog con tus amigos
También tengo canal de YouTube: https://www.youtube.com/channel/UCroP4BTWjfM0CkGB6AFUoBg?sub_confirmation=1
Twitter: https://twitter.com/parzibyte
Facebook: https://facebook.com/parzibyte.fanpage
Instagram: https://instagram.com/parzibyte
Hacer una donación vía PayPal: https://paypal.me/LuisCabreraBenito
------------------------------------------------------------------------------------------------
*/ ?>
<?php
// Lo mismo que "establecer_modo_compra" pero ahora la orden es "r" de recargar
include_once "constantes.php";
$saldo = $_GET["saldo"];
file_put_contents(ARCHIVO_RESPUESTA_LECTOR, ""); // Limpiar lo que haya dicho la ESP8266
file_put_contents(ARCHIVO_ORDEN_LECTOR, "r;$saldo"); // Le indicamos al lector la orden (recargar) y el saldo a recargar
echo json_encode(true);

Y la respuesta del lector se va a manejar del mismo modo que cuando hacíamos compras.

Poniendo todo junto

Así es como funciona todo este pequeño proyecto de monedero o saldo electrónico con tarjetas RFID y PHP. Me siento muy orgulloso de haberlo realizado, ya que se me hizo difícil al inicio y no me había enfrentado a algo así.

Yo utilicé PHP pero puedes portarlo fácilmente a Go, Java, C#, etcétera; todo depende de lo que tú necesitas.

Te dejo el código fuente completo en mi GitHub. Más adelante traeré un vídeo de YouTube para mostrarte una pequeña demostración.

También te invito a leer más sobre Electrónica y PHP en mi blog.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

3 días hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

3 días hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

3 días hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

3 días hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

3 días hace

Solución: Apache – Server unable to read htaccess file

Ayer estaba editando unos archivos que son servidos con el servidor Apache y al visitarlos…

4 días hace

Esta web usa cookies.