草庐IT

php - 将二进制命令从 PHP 发送到 Arduino 驱动的热敏打印机

coder 2024-04-08 原文

我正在玩 Arduino(Uno rev 3)和热敏打印机(此型号 https://www.sparkfun.com/products/10438),玩得很开心。 Arduino 每 10 秒向我的本地机器发出一个请求(通过以太网屏蔽)并将响应(如果 200)存储在 SD 卡上。然后它使用这个库打印出来 https://github.com/adafruit/Adafruit-Thermal-Printer-Library .

到目前为止,我可以正确地轮询、存储和打印基本文本,但现在我正在尝试使用一些更高级的命令(下划线、反转等)。我的最终目标是向下发送图像并处理服务器上的所有渲染 http://printer.gofreerange.com/ .

问题是我发送的命令被输出为文本字符。有些命令有效(换行),但其他命令出现乱码。我附上了 Arduino 代码和它调用的基本 PHP 脚本。有帮助吗?

阿杜伊诺:

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>
#include <SoftwareSerial.h>
#include "Adafruit_Thermal.h"

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
const char host[] = "192.168.1.100";
char cacheFilename[] = "TMP";

const byte printer_RX_Pin = 8; // this is the green wire
const byte printer_TX_Pin = 9; // this is the yellow wire
const byte SD_Pin = 4;         // the SD Card SPI pin

bool downloadWaiting = false;
bool statusOk = false;
unsigned long content_length = 0;

EthernetClient client;
Adafruit_Thermal printer(printer_RX_Pin, printer_TX_Pin);


void die(unsigned int times) {
  while(true);
}


void checkForDownload() {

  Serial.println("checkForDownload");

  content_length = 0;
  statusOk = false;
  unsigned long length = 0;

  if (SD.exists(cacheFilename)) {
    if (!SD.remove(cacheFilename)) {
      die(4);
    }
  }
  File cache = SD.open(cacheFilename, FILE_WRITE);

  if(client.connect(host, 80)) {

    client.println("GET /printer.php HTTP/1.1");
    client.print("Host: "); client.println(host);
    client.println("User-Agent: arduino-ethernet");
    client.println("Connection: close");
    client.println();

    bool parsingHeader = true;

    while(client.connected()) {
      while(client.available()) {

        if (parsingHeader) {

          client.find((char*)"HTTP/1.1 ");
          char statusCode[] = "000";
          client.readBytes(statusCode, 3);
          statusOk = (strcmp(statusCode, "200") == 0);

          client.find((char*)"Content-Length: ");
          char c;
          while (isdigit(c = client.read())) {
            content_length = (content_length * 10) + (c - '0');
          }

          client.find((char*)"\n\r\n");
          parsingHeader = false;

        } else {
          if(length < content_length) {
            cache.write((byte)client.read());
            length++;
          } else {
              client.read();
          }
        }

      }
    }

    client.stop();
    cache.seek(0);

    if (statusOk && content_length > 0 && (content_length == length) && (content_length == cache.size())) {
      downloadWaiting = true;
    }

  } else {
    client.stop();
  }

  cache.close();

}


void printFromDownload() {

  Serial.println("printFromDownload");

  File cache = SD.open(cacheFilename);
  byte b;

  while (content_length--) {
    printer.write((byte)cache.read());
  }

  printer.feed();

  cache.close();
  downloadWaiting = false;

}


void setup(){

  pinMode(SD_Pin, OUTPUT);
  if (!SD.begin(SD_Pin)) {
    die(2);
  }

  if (Ethernet.begin(mac) == 0) {
    die(3);
  }

  Serial.begin(9600);
  printer.begin(255);

  delay(1000);

}


void loop() {
  if (downloadWaiting) {
    printFromDownload();
    delay(5000);
  } else {
    checkForDownload();
    if (!downloadWaiting) {
      delay(10000);
    }
  }
}

PHP:

<?php

ob_start();


// Turn on Inverse mode
// Doesn't work
echo pack('S', 29);
echo pack('S', 66);
echo pack('S', 1);

$string = 'Testing 1, 2, 3';

foreach(str_split($string) as $char) {
  echo pack('S', ord($char)); // works
}

// Turn off Inverse mode
echo pack('S', 29);
echo pack('S', 66);
echo pack('S', 0);

// Line feed
echo pack('S', 10); // works

$content = ob_get_clean();
$length = strlen($content);
header("Content-Length: $length");

echo $content;

最佳答案

好像不能用printer.write()直接打印位图数据。正如您在 printBitmap() 方法中所见,打印机需要一些特殊字节来打开位图打印模式。 (writeBytes(18, 42, chunkHeight, rowBytesClipped))

void Adafruit_Thermal::printBitmap(
 int w, int h, const uint8_t *bitmap, bool fromProgMem) {
  int rowBytes, rowBytesClipped, rowStart, chunkHeight, x, y, i;

  rowBytes        = (w + 7) / 8; // Round up to next byte boundary
  rowBytesClipped = (rowBytes >= 48) ? 48 : rowBytes; // 384 pixels max width

  for(i=rowStart=0; rowStart < h; rowStart += 255) {
    // Issue up to 255 rows at a time:
    chunkHeight = h - rowStart;
    if(chunkHeight > 255) chunkHeight = 255;

    writeBytes(18, 42, chunkHeight, rowBytesClipped);

    for(y=0; y < chunkHeight; y++) {
      for(x=0; x < rowBytesClipped; x++, i++) {
        PRINTER_PRINT(fromProgMem ? pgm_read_byte(bitmap + i) : *(bitmap+i));
      }
      i += rowBytes - rowBytesClipped;
    }
    timeoutSet(chunkHeight * dotPrintTime);
  }
  prevByte = '\n';
}

您的草图需要了解来自 PHP 的数据,并知道何时使用 printer.write() 将单个字符作为字节发送以及何时使用 printer.printBitmap() 将字节作为图像发送。通过这种方式,打印机接收到正确的命令以准备打印适当的数据。您需要围绕要在 PHP 中打印的内容构建一些元数据,并将其发送到 Arduino。 JSON 格式可能如下所示:

{"reciept": [
  {
    "type": "text",
    "style": "bold",
    "value": "Thank you for your purchase"
  },
  {
    "type": "bitmap",
    "pos": "center",
    "value": ".... binary data ..."
  }
]}

现在您的 Arduino 草图将了解何时将字节作为文本单独发送以及何时将大量数据作为位图发送。

更紧凑的格式可能使用换行符作为段之间的分隔符:

F|bold
T|Thank you for shopping with us\r
P|Center
B|...binary data (with \r escaped)... \r

或者,您可以发送每个段的数据量,以避免像 HTTP 的 Content-Length header 一样转义二进制数据

F4|boldT32|Thank you for shopping with us\rP6|CenterB3000|...binary data...

关于php - 将二进制命令从 PHP 发送到 Arduino 驱动的热敏打印机,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14082184/

有关php - 将二进制命令从 PHP 发送到 Arduino 驱动的热敏打印机的更多相关文章

  1. ruby - 在 Ruby 中编写命令行实用程序 - 2

    我想用ruby​​编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序

  2. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  3. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  4. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  5. Ruby - 如何将消息长度表示为 2 个二进制字节 - 2

    我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi

  6. FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D) - 2

    本教程将在Unity3D中混合Optitrack与数据手套的数据流,在人体运动的基础上,添加双手手指部分的运动。双手手背的角度仍由Optitrack提供,数据手套提供双手手指的角度。 01  客户端软件分别安装MotiveBody与MotionVenus并校准人体与数据手套。MotiveBodyMotionVenus数据手套使用、校准流程参照:https://gitee.com/foheart_1/foheart-h1-data-summary.git02  数据转发打开MotiveBody软件的Streaming,开始向Unity3D广播数据;MotionVenus中设置->选项选择Unit

  7. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

  8. ruby - 从 Ruby : capturing the output while displaying the output? 运行 shell 命令 - 2

    我有一个问题。我想从另一个ruby​​脚本运行一个ruby​​脚本并捕获它的输出信息,同时让它也输出到屏幕。亚军#!/usr/bin/envrubyprint"Enteryourpassword:"password=gets.chompputs"Hereisyourpassword:#{password}"我运行的脚本文件:开始.rboutput=`runner`putsoutput.match(/Hereisyour(password:.*)/).captures[0].to_s正如您在此处看到的那样,存在问题。在start.rb的第一行,屏幕是空的。我在运行程序中看不到“输入您的密

  9. ruby - 未定义的方法 auto_upgrade!将 Sinatra/DataMapper 应用程序推送到 Heroku 时 - 2

    有谁知道在Heroku的Bamboo堆栈上启动并运行使用DataMapper的Sinatra应用程序所需的魔法咒语?Bamboo堆栈不包含任何预安装的系统gem,无论我尝试使用何种gem组合,我都会不断收到此错误:undefinedmethod`auto_upgrade!'forDataMapper:Module(NoMethodError)这是我的.gems文件中的内容:sinatrapgdatamapperdo_postgresdm-postgres-adapter这些是我将应用程序推送到Heroku时安装的依赖项:----->Herokureceivingpush----->Si

  10. ruby - ruby 脚本可以预编译成二进制文件吗? - 2

    我正在开发一个Ruby脚本,需要在没有Ruby解释器的情况下部署到系统上。它将需要在使用ELF格式的FreeBSD系统上运行。我知道有一个ruby​​2exe项目可以编译在Windows上运行的ruby​​脚本,但是在其他操作系统上这样做容易吗?甚至可能吗? 最佳答案 您是否检查过Rubinius或JRuby是否允许您预编译您的代码? 关于ruby-ruby脚本可以预编译成二进制文件吗?,我们在StackOverflow上找到一个类似的问题: https://

随机推荐