数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第1张图片

转发: Digital QR Scanner (QR Code Recorder & QR Code Classify & Chip Counter all in one )


项目介绍

我开发了一款可以扫描条形码和二维码的手持设备。 我称之为“数字 QR 扫描仪”。 扫描条码后,可以直接存入TF,也可以将本次扫描的所有条码按照货物的“型号/批次/日期代码/数量”进行分类。 排序结果可以通过WiFi模块连接云打印机打印出来。 并且可以进行芯片计数。

我使用Raspberry Pi的RP2040作为MCU和WiFi模块WizFi360连接网络打印机; 作为手持设备,我设计了一个内置可充电锂电池的按键开关电路; TF卡用于存储扫描条码的结果; 透明OLED屏(SSD1309)用于状态显示; 一个可以Uart输出的二维码扫描模块,3个触摸按钮和一个操纵杆......

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第2张图片

新器件、新应用电路使用较多,硬件调试时间较长。

我使用PCB作为材料来设计这个设备。 4层PCB三明治结构包含了所有模块和器件,不仅增强了结构强度,而且还充当芯片计数的隧道。

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第3张图片

基于这个透明的OLED屏幕,我这次使用了新的动画菜单,每个功能都有相应的动画效果。 如下:

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第4张图片

基于这个透明的OLED屏幕,我这次使用了新的动画菜单,每个功能都有相应的动画效果。 如下:

typedef enum 
{
  DO_Menu = 0,
  DO_QR_Scan,
  DO_QR_Classify,
  DO_Chip_Counter,
  DO_Setting,
  DO_QR_Classify_Print
}STATE_;
STATE_ CurrentState;

要显示“DO_Menu”中的主菜单,请单击触摸按钮的左按钮,Touch_Key_Num 将增加。 单击触摸按钮的右键,Touch_Key_Num 会减小。 然后根据Touch_Key_Num的值显示相应的菜单和图标。

  case DO_Menu:
     {
        display.clearDisplay();
        if(Touch_Key_Num%4 == 0)
        {
          display.drawBitmap(8, 8, frames_QR[frame], FRAME_WIDTH, FRAME_HEIGHT, 1);
          display.setTextColor(SSD1306_WHITE);
          display.setTextSize(1);
          display.setCursor(65, 20);
          display.print("QR Code");
          display.setCursor(65, 30);
          display.print("Scanner");
          display.display();
          frame = (frame + 1) % FRAME_COUNT_QR;
          delay(FRAME_DELAY);
          Touch_handling();
          delay(FRAME_DELAY);
          if(Touch_Key_Enter)
          {
            CurrentState = DO_QR_Scan;
            display.clearDisplay();
            display.setTextColor(SSD1306_WHITE);
            display.setTextSize(1);
            display.setCursor(18, 1);
            display.print("QR Code Scanner");
            display.drawBitmap(4, 8, frames_tick[0], FRAME_WIDTH, FRAME_HEIGHT, 1);
            display.fillRect(0, 55, 128, 8, SSD1306_WHITE);
            Touch_Key_Enter = false;
          }
        }
        else if(Touch_Key_Num%4 == 1)
        {          
          display.drawBitmap(8, 8, frames_Classify[frame], FRAME_WIDTH, FRAME_HEIGHT, 1);
          display.setTextColor(SSD1306_WHITE);
          display.setTextSize(1);
          display.setCursor(65, 20);
          display.print("QR Code");
          display.setCursor(65, 30);
          display.print("Classify");
          display.display();
          frame = (frame + 1) % FRAME_COUNT_Classify;
          delay(FRAME_DELAY);
          Touch_handling();
          delay(FRAME_DELAY);
          if(Touch_Key_Enter)
          {
            CurrentState = DO_QR_Classify;
            display.clearDisplay();
            display.setTextColor(SSD1306_WHITE);
            display.setTextSize(1);
            display.setCursor(16, 1);
            display.print("QR Code Classify");      
            display.drawBitmap(4, 8, frames_tick[0], FRAME_WIDTH, FRAME_HEIGHT, 1);
            display.drawRect(56, 12, 68, 11, SSD1306_WHITE);
            display.drawRect(56, 22, 68, 11, SSD1306_WHITE);
            display.drawRect(56, 32, 68, 11, SSD1306_WHITE);
            display.drawRect(56, 42, 68, 11, SSD1306_WHITE);
            display.setTextColor(SSD1306_WHITE);
            display.setCursor(58, 14);
            display.print("MODEL");
            display.setCursor(58, 24);
            display.print("Lot");
            display.setCursor(58, 34);
            display.print("D/C");
            display.setCursor(58, 44);
            display.print("Qty");
            display.drawRoundRect(92, 14, 22,7,7,SSD1306_WHITE);
            display.drawRoundRect(92, 24, 22,7,7,SSD1306_WHITE);
            display.drawRoundRect(92, 34, 22,7,7,SSD1306_WHITE);
            display.drawRoundRect(92, 44, 22,7,7,SSD1306_WHITE);
            display.fillRect(0, 55, 128, 8, SSD1306_WHITE);
            Touch_Key_Enter = false;
          }
        }        
        else if(Touch_Key_Num%4 == 2)
        {
          display.clearDisplay();
          display.drawBitmap(8, 8, frames_Counter[frame], FRAME_WIDTH, FRAME_HEIGHT, 1);
          display.setTextColor(SSD1306_WHITE);
          display.setTextSize(1);
          display.setCursor(65, 20);
          display.print("Chip");
          display.setCursor(65, 30);
          display.print("Counter");
          display.display();
          frame = (frame + 1) % FRAME_COUNT_Counter;
          delay(FRAME_DELAY);
          if(Touch_Key_Enter)
          {
            CurrentState = DO_Chip_Counter;
            display.clearDisplay();
            display.setTextColor(SSD1306_WHITE);
            display.setTextSize(1);
            display.setCursor(32, 1);
            display.print("Chip Counter");
            display.drawBitmap(8, 8, frames_CountX[0], FRAME_WIDTH, FRAME_HEIGHT, 1);
            display.display();
            Touch_Key_Enter = false;
          }
        }
        else if(Touch_Key_Num%4 == 3)
        {
          display.clearDisplay();
          display.drawBitmap(8, 8, frames_setting[frame], FRAME_WIDTH, FRAME_HEIGHT, 1);          
          display.setTextColor(SSD1306_WHITE);
          display.setTextSize(1);
          display.setCursor(65, 25);
          display.print("Setting");          
          display.display();
          frame = (frame + 1) % FRAME_COUNT_setting;
          delay(FRAME_DELAY);
          if(Touch_Key_Enter)
          {
            CurrentState = DO_Setting;
            Touch_Key_Enter = false;
          }
        }
     }
    break;

我将根据3个主要功能来介绍“数字二维码扫描仪”的开发过程。
三个主要功能是:
1.二维码扫描并记录;
2、二维码分类打印;
3、芯片计数;

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第5张图片

1,QR code Scan and record;

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第6张图片

在菜单上选择“二维码扫描仪”,然后单击触摸按钮的中间按钮,进入“二维码扫描仪模式”。

在二维码扫描模式下,单击中间按钮扫描二维码。 如果成功,左边的方框会被选中,然后右边的数字会加一,屏幕底部会显示本次扫描的结果。
屏幕右侧的两个数字显示扫描计数,上一位为当前计数,下一位为总计数。 右键可以清除当前计数,长按可以清除总计数。

如果扫描失败,屏幕左侧的框中将出现一条错误消息,您需要重新扫描。 如下:

成功识别二维码后,将保存在TF卡中的“recorder.txt”文件中,如下图所示;

二维码存储格式如下:

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第7张图片

二维码扫描模式代码如下:

  case DO_QR_Scan:
    {      
      if(Touch_Key_Enter == true)
      {
        display.drawBitmap(4, 8, frames_tick[1], FRAME_WIDTH, FRAME_HEIGHT, 0);
        display.drawBitmap(4, 8, frames_tick[0], FRAME_WIDTH, FRAME_HEIGHT, 1);        
        display.fillRect(0, 55, 128, 8, SSD1306_WHITE);
        digitalWrite(PIN_QR_TRIG, LOW);
        Scan_En = true;
        Touch_Key_Enter = false;
      }
      if(Scan_En)
      {
        if( millis()- Time_Touch_Key > 6000)
        {
          digitalWrite(PIN_QR_TRIG, HIGH);
          display.writeLine(17, 21, 37, 41,SSD1306_WHITE);
          display.writeLine(17, 41, 37, 21,SSD1306_WHITE);
        }
        if( millis()- Time_Touch_Key > 10000)
        {
          display.writeLine(17, 21, 37, 41,SSD1306_BLACK);
          display.writeLine(17, 41, 37, 21,SSD1306_BLACK);  
          Scan_En = false;
        }          
      }
      while (QR.available())
      {
        char a = QR.read();
        Serial.print(a);
        data_String += a;
        digitalWrite(PIN_QR_TRIG, HIGH);
        delay(10);
      }
      if(data_String != "")
      {        
        display.drawBitmap(4, 8, frames_tick[0], FRAME_WIDTH, FRAME_HEIGHT, 0);
        display.drawBitmap(4, 8, frames_tick[1], FRAME_WIDTH, FRAME_HEIGHT, 1);
        display.setTextColor(SSD1306_BLACK);
        display.setTextSize(2);
        display.setCursor(65, 15);
        display.println(QR_num_Now);
        display.setCursor(65, 35);
        display.println(QR_num);
        QR_num ++;
        QR_num_Now ++;

        display.setTextColor(SSD1306_BLACK);
        display.setTextSize(1);
        display.setCursor(1, 56);
        display.print(data_String);
        
        myFile = SD.open(file_name, FILE_WRITE);
        if (myFile) {
          myFile.println(data_String);
          myFile.close();
          data_String = "";  
        }
        Scan_En = false;
      }
      display.setTextColor(SSD1306_WHITE);
      display.setTextSize(2);
      display.setCursor(65, 15);
      display.println(QR_num_Now);
      display.setCursor(65, 35);
      display.println(QR_num);
      display.display();
    }
    break;

2、二维码分类打印;

在“二维码分类模式”下,逐一扫描每个二维码,按照“型号--批次--日期代码--数量”的顺序扫描二维码。 扫描所有二维码后,即可将所有商品按类别进行汇总。 并输出存储或打印。

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第8张图片

如上图所示,屏幕右侧有一个扫描顺序指示符。 当当前需要的二维码被正确扫描后,该类别对应的框将会被填充。

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第9张图片

这是我们出库时箱子表面的二维码标签。 我通过判断“P/N”、“LOT”、“D/C”、“QTY”等字符来判断是否是本次扫描需要的二维码。 代码,
代码如下所示:

        if(DO_QR_Classify_Step == 0)
        {
          if(data_String.substring(0, data_String.indexOf(":", 0)) == "P/N")
          {
            Product_Box[Product_Box_cnt].MODEL = data_String.substring(data_String.indexOf(":", 0)+1, data_String.length()-1);    
            display.fillRoundRect(95, 16, 16,3,3,SSD1306_WHITE);           
            DO_QR_Classify_Step ++;
          }
        }else if(DO_QR_Classify_Step == 1)
        {          
          if(data_String.substring(0, data_String.indexOf(":", 0)) == "LOT")
          {
            Product_Box[Product_Box_cnt].LOT = data_String.substring(data_String.indexOf(":", 0)+1, data_String.length()-1);
            display.fillRoundRect(95, 26, 16,3,3,SSD1306_WHITE);
            DO_QR_Classify_Step ++;            
          }          
        }else if(DO_QR_Classify_Step == 2)
        {
          Product_Box[Product_Box_cnt].DC = data_String.substring(data_String.indexOf(":", 0)+1, data_String.length()-1);
          
          if(data_String.substring(0, data_String.indexOf(":", 0)) == "D/C")
          {
            display.fillRoundRect(95, 36, 16,3,3,SSD1306_WHITE);
            DO_QR_Classify_Step ++;
          }
        }else if(DO_QR_Classify_Step == 3)
        {
          if(data_String.substring(0, data_String.indexOf(":", 0)) == "QTY")
          {
              Product_Box[Product_Box_cnt].Qty = data_String.substring(data_String.indexOf(":", 0)+1, data_String.length()-1).toInt();
              display.fillRoundRect(95, 46, 16,3,3,SSD1306_WHITE);
              myFile = SD.open(file_name, FILE_WRITE);
              if (myFile) {
                myFile.print("BOX No:");
                myFile.println(Product_Box_cnt);
                myFile.print("MODEL:");
                myFile.println(Product_Box[Product_Box_cnt].MODEL);
                myFile.print("LOT:");
                myFile.println(Product_Box[Product_Box_cnt].LOT);
                myFile.print("D/C:");
                myFile.println(Product_Box[Product_Box_cnt].DC);
                myFile.print("QTY:");
                myFile.println(Product_Box[Product_Box_cnt].Qty);
                myFile.println("");
                myFile.close();
              }        
              DO_QR_Classify_Step = 0;
              Product_Box_cnt ++;
              display.fillRoundRect(92, 14, 22,7,7,SSD1306_BLACK);
              display.fillRoundRect(92, 24, 22,7,7,SSD1306_BLACK);
              display.fillRoundRect(92, 34, 22,7,7,SSD1306_BLACK);
              display.fillRoundRect(92, 44, 22,7,7,SSD1306_BLACK);
              display.drawRoundRect(92, 14, 22,7,7,SSD1306_WHITE);
              display.drawRoundRect(92, 24, 22,7,7,SSD1306_WHITE);
              display.drawRoundRect(92, 34, 22,7,7,SSD1306_WHITE);
              display.drawRoundRect(92, 44, 22,7,7,SSD1306_WHITE);   
          }       
        }     

每个扫描成功的二维码都会显示在屏幕底部; 并判断是否是所要求的内容。 如果出现错误,就会卡在本次扫描中。 如果您想退出此扫描,请单击左侧按钮退出此扫描并重试。 扫描,当扫描成功“型号--LOT--DateCode--Quantity”四个类别的二维码后,将存储在TF卡中。

#define Product_Box_max 64

struct _Product_Box{
  String MODEL;
  String LOT;
  String DC;
  uint32_t Qty;
} ;
_Product_Box Product_Box[Product_Box_max];

比如下图中的四个标签,

扫描后存储在TF卡中的格式为:

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第10张图片

如果长按左键,会进入二维码分类模式,仍然按照“型号--LOT--DateCode”分类,同一类别的商品数量会累加在一起。 仍然使用图中的例子,长按左键后,TF卡会总结输出如下:

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第11张图片

为了节省电池电量,WiFi 模块在正常情况下处于关闭状态。 如果在设置中设置了打印选项,数字二维码扫描仪将打开 WiFi 模块并通过网络连接到云打印机。 此扫描的摘要结果也将被打印。

打印部分代码如下:

  case DO_QR_Classify_Print:
    {
       switch(Wifi_State){
        case Power_On_wifi:
          {
            Serial.println("POWER_ON1");
            digitalWrite(PIN_Wifi_RST, HIGH);
            delay(1000);
            WiFi.init(&Serial2);
            Serial2.flush();
            Serial2.begin(2000000);
            // attempt to connect to WiFi network
            while ( status != WL_CONNECTED) {
              // Connect to WPA/WPA2 network
              status = WiFi.begin(ssid, pass);
            }
            // print your WiFi shield's IP address
            ip = WiFi.localIP(); 
            Wifi_State = Link_To_Network;
          }
          break;
        case Link_To_Network:
          {
             if (client1.connect(Cloud_Printer,80)){
                delay(3000);                
                client1.print(String("TEXT")+String("QR CODE Classify"));
                Wifi_State = QR_Code_print;
             }
          }
          break;
        case QR_Code_print:
          {
          }
          break;
       }
    }
    break;

3、芯片计数;  

芯片计数功能主要由3组红外发射管和接收管处理,其中2组用于计数,另外一组红外发射管和接收管用于判断当前位置是否有芯片 磁带的。

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第12张图片

芯片编织层的侧面有一些小孔,每个芯片对应三个孔。 所以通过数孔的数量来计算芯片的数量。

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第13张图片

为了准确识别数量,我使用两层PCB设计了一个隧道。

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第14张图片

我用了两个中断函数,分别对应两个红外接收管的IO。

  pinMode(PIN_IR_1, INPUT);
  attachInterrupt(digitalPinToInterrupt(PIN_IR_1), IR_COUNTER, CHANGE);
  pinMode(PIN_IR_2, INPUT);
  attachInterrupt(digitalPinToInterrupt(PIN_IR_2), IR_COUNTER, CHANGE);

如果正常的话,两个IO的编码应该是00-11-01-10的状态。这意味着有一个小孔穿过两个红外管,计数+1。

void IR_COUNTER()
{
  IR_current = ((IR_current << 2) | (digitalRead(PIN_IR_1) << 1) | (digitalRead(PIN_IR_2)));
  Serial.println((digitalRead(PIN_IR_1) << 1) | (digitalRead(PIN_IR_2)), HEX);
  if ((IR_current == 0b00101101) || (IR_current == 0b11101101)|| (IR_current == 0b00100001) || (IR_current == 0b00101001)|| (IR_current == 0b10001000))
  {
    Hole_num ++ ;
//    Serial.print("NUM:");
//    Serial.println(Hole_num);
  }
}

整个芯片计数部分的代码如下:

  case DO_Chip_Counter:
    {
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);
      display.setTextSize(1);
      display.setCursor(32, 1);
      display.print("Chip Counter");
      if(Hole_num > Hole_num_Now)
      {
        display.drawBitmap(8, 8, frames_CountX[frame], FRAME_WIDTH, FRAME_HEIGHT, 1);
        frame = (frame + 1) % FRAME_COUNT_CountX;
        if(frame == FRAME_COUNT_CountX-1)
        {
          Hole_num_Now = Hole_num;
        }
      }
      else
      {
        display.drawBitmap(8, 8, frames_CountX[0], FRAME_WIDTH, FRAME_HEIGHT, 1);    
      }
      display.setTextColor(SSD1306_WHITE);
      display.setTextSize(2);
      display.setCursor(65, 25);
      display.println(Hole_num/3); 
      display.display();
      delay(FRAME_DELAY);
    }
    break;

对于云打印机部分,我使用了上次开发 Any 打印机项目时的代码。 其中代码只需要创建Socket并发送以TEXT为前缀的数据,发送的数据会直接打印出来。

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第15张图片

嵌入式网页和跨平台输入程序正在开发中。

其中,跨平台输入功能进展顺利。 通过电脑端的Python程序,接收数字二维码扫描仪的扫描结果,并自动填充EXCEL上的相应位置,我认为是一个比较实用的功能。

数字 QR 扫描仪(QR 码记录器、QR 码分类和芯片计数器三合一)_第16张图片

待续。

文件

  • Digital_QR_code_Scannner_arduino_code

你可能感兴趣的:(分类,javascript,数据挖掘,RP2040,W5100S,WIZnet,wizfi360)