从PHP发送二进制命令到Arduino供电的热敏打印机

时间:2012-12-29 13:34:52

标签: php arduino

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

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

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

Arduino的:

#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;

1 个答案:

答案 0 :(得分:0)

您似乎无法直接使用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标头一样

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