硬件准备:
esp32主板:一块; RFID-RC522 感应主板 :一块 ; IC卡: 两张 ;杜邦线 :7根
连接电路:
RST 22
SS/SDA 21
MISO 27
MOSI 26
SCK 25
IRQ 不用接
软件编程:
1、vs code 增加库
2、代码部分
#include <Arduino.h>
#include <iostream>
#include <string.h>
#include <MFRC522.h>
#include <sstream>
#include <SPI.h>
//注意上面连接电路
#define RST_PIN 22 //GPIO22
#define SS_PIN 21 //GPIO21 SDA
#define MISO_PIN 27 //GPIO27 spi 需要引脚
#define MOSI_PIN 26 //GPIO26
#define SCK_PIN 25 //GIPO25
#define blockAddr 4 //For example; if blockAddr is 03h then pages 03h, 04h, 05h, 06h are returned.
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
MFRC522::MifARE_Key key;
void dump_byte_array(byte *buffer, byte bufferSize);
bool isData(byte *buffer, byte *oldBuffer);
void setup(){
Serial.begin(115200);
Serial.println("2222");
SPI.begin(SCK_PIN,MISO_PIN,MOSI_PIN); // Init SPI bus
SPI.setDataMode(SPI_MODE3);
mfrc522.PCD_Init(); // Init MFRC522 card
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
byte* b=new byte[4];
b[0]=0xFF;
b[1]=0x01;
b[2]=0x01;
b[3]=0xFF;
dump_byte_array(b,4);
dump_byte_array(key.keyByte,MFRC522::MF_KEY_SIZE);
}
void loop(){
if(!mfrc522.PICC_IsNewCardPresent()){ //发现新卡的跳出
return;
}
if(!mfrc522.PICC_ReadCardSerial()){ //选择一张卡
return;
}
Serial.println("发现一张新卡");
//获取卡的id
Serial.print("新卡id===>");
dump_byte_array(mfrc522.uid.uidByte,mfrc522.uid.size);
//开始写入
byte dataBlock[] = {
0x15, 0x02, 0x00, 0x01, // 5, 2, 0, 1,
0x03, 0x01, 0x04, 0x08, // 3, 1, 4, 8,
0x09, 0x0a, 0xff, 0x0b, // 9, 10, 255, 11,
0x0c, 0x0d, 0x0e, 0x10 // 12, 13, 14, 15
};
byte trailerBlock = 7;
Serial.print("构造数据:");
dump_byte_array(dataBlock, sizeof(dataBlock));
Serial.println();
//开始身份认证
MFRC522::StatusCode status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) {
Serial.print(F("PCD_Authenticate() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}else{
Serial.println("------>");
}
//开始读取数据
byte buffer[18]; //这里必须是长度18
byte size = sizeof(buffer);
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("MIFARE_Read() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
}else{
Serial.print(F("MIFARE_Read() ok "));
}
Serial.print(F("Data in block "));
dump_byte_array(buffer, sizeof(buffer));
Serial.println("n==================*********=====================");
//判断是否需要写入数据
if(!isData(buffer,dataBlock)){
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("====================>MIFARE_Write() failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
}else{
Serial.println("写入成功");
}
Serial.println();
}else{
Serial.println("已经写入过了不能再写入了");
}
// Halt PICC
mfrc522.PICC_HaltA();
// Stop encryption on PCD
mfrc522.PCD_StopCrypto1();
}
//比较数据是否同样
bool isData(byte *buffer, byte *oldBuffer){
bool is=false;
if(sizeof(buffer)==sizeof(oldBuffer)){
int lSize=sizeof(buffer);
for(int i=0;i<lSize;i++){
if(buffer[i]==oldBuffer[i]){
is=true;
}else{
is=false;
break;
}
}
}else{
is=false;
}
return is;
}
//打印
void dump_byte_array(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(" ");
Serial.print(buffer[i]);
}
}
显示效果:
门禁管理软件开发总结
一、定制需求简述:
门禁管理软件向设备传输数据时,上传人员信息及指纹信息不全,由于门禁管理软件是按照固件版本来处理下发指纹数据,因为以前的软件对于现在的机器固件的指纹下发是处理不了的。因此需要定制。
门禁管理软件
二、实现方法:
因上传人员组合验定时,在高速下是无法成功下发。原因为,在高速上传用户时,是先写到内存中,最后提交的,而上传人员组合验证的方法,不支持高速,它会直接下发到机器,这时机器中还没有人员,所以会失败。因此,在高速上传时,上传人员组合验证的功能移到最后,待人员都上传完毕之后处理。
三、门禁管理软件代码Changelog:
主要代码如下:
UploadOk := ZkManager.UpdateBatch;
//更新组合验证 不可以放到高速里面,只有在高速完成了之后再做
//如果是高速模式还要处理 上传验证方式
First; //从头开始
while Not Eof do
begin
if Not FieldByName('Selected').AsBoolean then //此用户没有选中跳过
begin
Next;
Continue;
Application.ProcessMessages;
end;
if ckbBase.Checked then
begin
if FCancelOp then Exit;
ZeroMemory(Pointer(@UserACCfg),Sizeof(UserACCfg));
//处理验证方式
UserACCfg.VerifyStyle := 0;
//根据门禁管理软件参数[上传用户没有指定门禁权限时,门禁管理软件将默认权限(使用组1时间段)。],
//去查找此用户有没有定义用户门禁权限
bFind := SearchUserDoorPri(ZkManager.DeviceInfo.ID, IntToStr(FieldByName('UserNo').AsInteger), UserACCfg);
if not bFind then
begin
//没有找到用户自定义门禁权限, 用户在不在:组->门禁组中
bFind := SearchUserDoorPriExt(ZkManager.DeviceInfo.ID, FieldByName('UserNo').AsInteger, UserACCfg);
end;
if bFind and ZkManager.TFTACC then
begin
iVerify:=IfThen((UserACCfg.VerifyStyle > 0), (128+UserACCfg.VerifyStyle-1), 0);
ZkManager.ZKem.SetUserInfoEx(ZkManager.DeviceInfo.MachineNumber, FieldByName('UserNo').AsInteger, iVerify, Reserved);
end;
最近遇到一个socke udp协议通讯的需求,而且是16进制数据接收。这样在传输参数的时候老是提示参数错误,因为计算机是不能直接传输16进制的,会自行转换,所有以下代码非常完美的解决我的问题,同时也让我认识到并不是所有socket都是需要一个客户端和服务端代码
<?php $sendStr = '01 49 03 01 01 00 4B'; // 16进制数据01 49 03 01 01 00 4B $sendStrArray = str_split(str_replace(' ', '', $sendStr), 2); // 将16进制数据转换成两个一组的数组 $socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP); // $socket = socket_create(AF_INET, SOCK_STREAM, getprotobyname("udp")); // 创建Socket if (socket_connect($socket, "192.168.16.254", 8080)) { //连接 for ($j = 0; $j < count($sendStrArray); $j++) { socket_write($socket, chr(hexdec($sendStrArray[$j]))); // 逐组数据发送 } $receiveStr = ""; $receiveStr = socket_read($socket, 1024, PHP_BINARY_READ); // 采用2进制方式接收数据 $receiveStrHex = bin2hex($receiveStr); // 将2进制数据转换成16进制 echo "client:" . $receiveStrHex; } socket_close($socket); // 关闭Socket ?>
运行文件返回如图所示
以下是两个文件的通讯案例,分别是udp_s.php和udp_c.php分别是服务端和客户端
udp_s.php文件
<?php
error_reporting( E_ALL );
set_time_limit( 0 );
ob_implicit_flush();
$socket = socket_create( AF_INET, SOCK_DGRAM, SOL_UDP );
if ( $socket === false ) {
echo "socket_create() failed:reason:" . socket_strerror( socket_last_error() ) . "n";
}
$ok = socket_bind( $socket, '127.0.0.1', 8080 );
if ( $ok === false ) {
echo "socket_bind() failed:reason:" . socket_strerror( socket_last_error( $socket ) );
}
while ( true ) {
$from = "";
$port = 0;
socket_recvfrom( $socket, $buf,1024, 0, $from, $port );
echo $buf;
usleep( 1000 );
}
udp_c.php文件
<?php
$sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
$msg = "hello";
$len = strlen($msg);
socket_sendto($sock, $msg, $len, 0, '127.0.0.1', 8080);
socket_close($sock);
运行文件如下,没刷新下udp_test.php文件,cmd打印出一个hello
在项目开发的过程中,为了保证代码质量,我们会使用诸多代码质量检测工具,这些工具或是在本地,或是在云端,虽然工具可以检测出异常问题,但是这些问题还是需要我们程序员来修复,如果我们不强制所有人必须修复异常问题,某些人可能就会跳过异常检测,将代码合入主干。为了解决这个问题,我们需要强制门禁系统,所谓门禁就是通过检测的才能合入,不通过检测的不能合入。GitLab本身就为我们提供了这样的设置,我们只需要配置检测规则就可以了。
在项目设置中,配置只允许流水线成功的合并请求
要实现流水线的执行,当然需要有执行进程和环境了。GitLab为我们提供了CI工具-GitLab Runner,通过配置GitLab Runner,将环境注册到GitLab上,提交代码的时候,GitLab才能使用执行环境来跑我们的检测功能。
虽然也可以通过docker进行安装,但是GitLab Runner只是一个二进制文件,下载后直接就可以在机器上运行,而且使用docker部署的话,执行命令也是在docker中,如果需要一些其他的环境配置,也必须在docker中安装,这是比较麻烦的。
https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64
# 拷贝下载的文件到 /usr/local/bin 目录下
mv gitlab-runner-linux-amd64 /usr/bin/gitlab-runner
# 增加执行权限
chmod +x gitlab-runner
# 安装生成配置
gitlab-runner install --user=root --working-directory=/data/gitlab-runner
# 启动服务
gitlab-runner start
安装以后我们需要把GitLab Runner注册到GitLab上,我们可以选择共享Runner和指定Runner的方式。共享Runner就是我们使用管理员账号获取的全局注册信息,注册后此Runner可以给所有代码库使用;指定Runner是给每一个仓库配置的特定Runner,只能跑本仓库的流水线。我建议大家注册成共享Runner,因为共享Runner也可以设置为多个代码库的Runner,这样比较方便。
gitlab-runner register
url和token就是上图中展示的,描述和tag自己随便输入,tag要记住,后面要使用的
# 这个是前台运行的命令,需要使用nohup转为后端运行
# 也有人说通过sudo注册的gitlab-runner直接就是后台运行
gitlab-runner run
在项目根目录创建 .gitlab-ci.yml 文件,内容可做如下配置:
stages:
- sonarcheck
sonarcheck:
stage: sonarcheck
script:
- echo $CI_PROJECT_DIR
- cd $CI_PROJECT_DIR
- sonar-scanner
tags:
- sonar
stages:指定流水线所有步骤
script:当前步骤的执行命令
$CI_PROJECT_DIR:默认环境变量,本次流水线执行时下载的代码目录
tags:使用的runner
如果想要查看所有内置的环境变量,可以做如下配置
script:
- export // linux下使用此命令
- 'dir env:' // Windows使用此命令
提交代码后,就会看见流水线自动执行
gitlab流水线配置好以后,我们就可以对接sonarqube来实现代码上库门禁了
请参考我们的上一篇文章:
点击流水线任务,可以看到执行阶段
如果要查看具体的执行日志,可以点击具体阶段进行查看
当执行完以后,可以提交合入请求
可以看到合入请求中会显示流水线任务状态,当流水线任务还未执行完成时,会看到如下界面
等流水线执行完成后,显示如下界面:
如果流水线执行失败,合并按钮不可点击,这样就保证了每次合入都必须通过检查。因为我们配置了master分支打包部署,因此当合并代码后,会自动启动打包部署流水线。
在配置的过程中,有一些需要了解的问题,Runner的配置和CI文件的配置。在CI文件的配置中需要注意缓存的处理,默认情况下每一个阶段的执行都会清理环境到最干净的状态,也就是生成的所有中间文件或者修改文件都会在下一阶段执行前恢复成与Git仓库同步的状态。针对这种情况,CI为我们提供了缓存机制,缓存分为本地缓存和服务器缓存,服务器缓存主要是为了解决分布式流水线执行,一般采用本地缓存就可以了。本地缓存会在本地生成一个cache目录,我们想要缓存某个阶段的文件或者目录,就在某个阶段配置cache,当然也可以在全局进行配置,这样缓存就是对每个阶段都生效的。我们在示例中只配置了缓存路径path,这样所有的流水线都会缓存到一个目录中,如果需要每个分支缓存到不同文件,那么就需要增加key的配置,除了配置key以外,还有缓存策略配置,默认的策略是下载/上传,但是在有些任务中,我们只需要使用上一阶段的缓存文件即可,因此我们可以配置为pull策略,这样就只会下载上一步的缓存文件,不会继续上传到缓存地址。
关于门禁系统代码(门禁系统代码)的内容今天就分享到这里了,感谢你花时间阅读本站内容,更多关于门禁系统代码(门禁系统代码)内容别忘了在本站进行查找哦,所涉及版权问题可联系本站管理员。