3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

M5Stackでプリンターを使う

Posted at

はじめに

仕事で関わっているRecord Meetingという自動議事メモ作成サービスでM5Stackを利用していますが、音声、画面以外のユーザーインターフェースとして、プリンターを使いたい時があります。

COET Record Meeting

比較的安価に入手できるサーマルプリンター ZJ-5805 Portable Bluetooth Thermal Receipt Printer をM5Stackにつないでみました。

ZJ-5805 Portable Bluetooth Thermal Receipt Printer

検証

初めにBluetoothSerialで接続しようと思いましたが、うまく接続できませんでしたのでBLEで接続します。

テストコードはこちら。

/**
 * Test print for BlueTooth Printer
 * Copyright 2020 fukuebiz
 * 
 * Licensed to the Apache Software  Foundation (ASF) under one or more
 * contributor  license agreements.  See  the NOTICE  file distributed
 * with  this  work  for  additional information  regarding  copyright
 * ownership.   The ASF  licenses this  file to  you under  the Apache
 * License, Version  2.0 (the  "License"); you may  not use  this file
 * except in  compliance with the License.   You may obtain  a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the  License is distributed on an  "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY  KIND, either  express or
 * implied.   See  the License  for  the  specific language  governing
 * permissions and limitations under the License.
 */
# include <M5Stack.h>
# include <BLEDevice.h>

# include <BLEUtils.h>
# include <BLEServer.h>
# include <BLE2902.h>

// debug log
# define log_d0(x) Serial.printf(x)
# define log_d(x, y) Serial.printf(x, y)
# define log_d2(x, y, z) Serial.printf(x, y, z)

# define PRINTER_NAME "BlueTooth Printer" // Printer BD Name
static esp_bd_addr_t peer_bd_addr = {0x11,0x22,0x33,0x44,0x55,0x66}; // Printer BD Adress

static String printer_service_uuid = "";
static String printer_characteristic_uuid = "";
std::map<std::string, BLERemoteService*> *pRemoteServices;
BLERemoteService *printerService;
std::map<std::string, BLERemoteCharacteristic*> *pRemoteCharacteristics;
BLERemoteCharacteristic *printerCharacteristic;
std::map<std::string, BLERemoteDescriptor*> *pRemoteDescriptors;
BLERemoteDescriptor *printerDescriptor;

volatile boolean deviceBleConnected = false; // flag BLE conneted

/*
 * Client callback
 */
class PrinterClientCallbacks : public BLEClientCallbacks
{
  void onConnect(BLEClient *pClient)
  {
    deviceBleConnected = true; // set ble connected flag
    log_d("%s connected\n", PRINTER_NAME);
  }

  void onDisconnect(BLEClient *pClient)
  {
    pClient->disconnect();
    deviceBleConnected = false; // clear ble connected flag
    log_d("%s disconnected\n", PRINTER_NAME);
  }
};

/*
 * Notify Callback
 */
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic,
    uint8_t *pData, size_t length, bool isNotify)
{
    log_d2("notifyCallbacklength=%d, isNofity=%d\n", length, isNotify);
    log_d("uuid: %s\n", pBLERemoteCharacteristic->getUUID().toString().c_str());
}

/*
 * Connect to printer
 */
bool connectToServer(BLEAddress pAddress)
{
    log_d("Create a connection to addr: %s\n", pAddress.toString().c_str());
    BLEClient *pClient = BLEDevice::createClient();

    log_d0(" – Client created\n");
    pClient->setClientCallbacks(new PrinterClientCallbacks());

    log_d0(" – Connecting to server…\n");
    if (pClient->connect(pAddress)) // connect to the remove BLE Server.
    {
        log_d0(" - Connected to printer\n");
    }
    else
    {
        log_d0(" - Connection failed\n");
        return false;
    }

    log_d0("Retrieving service UUIDs...\n");
    pRemoteServices = pClient->getServices();
    for (const auto &p : *pRemoteServices) {
        String uuid = p.first.c_str();
        log_d("UUID: %s\n", uuid.c_str());
        if (!uuid.endsWith("-0000-1000-8000-00805f9b34fb"))
        {
            printer_service_uuid = uuid;
        }
    }
    if (printer_service_uuid == "")
    {
        log_d0(" – Printer service not found\n");
        return false;
    }

    printerService = pRemoteServices->at(printer_service_uuid.c_str());
    if (printerService == nullptr)
    {
        log_d(" – Service not found (UUID: %s)\n", printer_service_uuid.c_str());
        return false;
    }
    else
    {
        log_d(" – Service found (UUID: %s)\n", printer_service_uuid.c_str());
    }

    // printer characteristic
    log_d0("Retrieving printer characteristics...");
    pRemoteCharacteristics = printerService->getCharacteristics();
    for (const auto &p : *pRemoteCharacteristics) {
        String uuid = p.first.c_str();
        log_d("UUID: %s\n", uuid.c_str());
    }
    if (pRemoteCharacteristics->size() > 0)
    {
        printerCharacteristic = printerService->getCharacteristic(pRemoteCharacteristics->begin()->first.c_str());
        if (printerCharacteristic == nullptr)
        {
            log_d(" – Notify characteristic not found (UUID: %s)\n", pRemoteCharacteristics->begin()->first.c_str());
            return false;
        }
        else
        {
            log_d(" – Notify characteristic found (UUID: %s)\n", pRemoteCharacteristics->begin()->first.c_str());
        }
    }
    else
    {
        log_d0(" – Notify characteristic not found\n");
        return false;
    }
    printerCharacteristic->registerForNotify(notifyCallback, true); //register callback

  return true;
}

/*
 * Scan device & log
 */
void scan()
{
    log_d0("UUID, Name, RSSI, Manufacturer\n");

    BLEScan* pBLEScan = BLEDevice::getScan();
    pBLEScan->setActiveScan(false);
    BLEScanResults foundDevices = pBLEScan->start(3);
    int count = foundDevices.getCount();
    for (int i = 0; i < count; i++)
    {
        BLEAdvertisedDevice d = foundDevices.getDevice(i);
        log_d0(d.getAddress().toString().c_str());
        log_d0(", ");
        log_d0(d.getName().c_str());
        log_d0(", ");
        log_d("%d", d.getRSSI());
        log_d0(", ");
        if (d.haveManufacturerData())
        {
            std::string data = d.getManufacturerData();
            int manu = data[1] << 8 | data[0];
            log_d("%d", manu);
        }
        log_d0("\n");
    }
}

void testCharacter()
{
    printerCharacteristic->writeValue(0x1b); // NORMAL TEXT
    printerCharacteristic->writeValue(0x21);
    printerCharacteristic->writeValue(0x00);
    printerCharacteristic->writeValue("[Font A]\n");
    printerCharacteristic->writeValue("!\"#$%&\'()*+,-./01234567890:;<=>?\n");
    printerCharacteristic->writeValue("@ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
    printerCharacteristic->writeValue(0x1b); // FONT Type B
    printerCharacteristic->writeValue(0x4d);
    printerCharacteristic->writeValue(0x01);
    printerCharacteristic->writeValue("[Font B]\n");
    printerCharacteristic->writeValue("!\"#$%&\'()*+,-./01234567890:;<=>?\n");
    printerCharacteristic->writeValue("@ABCDEFGHIJKLMNOPQRSTUVWXYZ\n");
}

void testJapanese()
{
    printerCharacteristic->writeValue(0x1b); // NORMAL TEXT
    printerCharacteristic->writeValue(0x21);
    printerCharacteristic->writeValue(0x00);
    printerCharacteristic->writeValue("[Chinese]\n");
    printerCharacteristic->writeValue(0x1c); // CHINESE MODE ON
    printerCharacteristic->writeValue(0x26);
    printerCharacteristic->writeValue({'\xc4', '\xe3', '\xba', '\xc3', '\x0a', '\0'}); // 你好
    printerCharacteristic->writeValue(0x1c); // CHINESE MODE OFF
    printerCharacteristic->writeValue(0x2e);
    printerCharacteristic->writeValue("[Japanese Hiragana]\n");
    printerCharacteristic->writeValue(0x1c); // CHINESE MODE ON
    printerCharacteristic->writeValue(0x26);
    printerCharacteristic->writeValue({'\xA4', '\xA2', '\xA4', '\xA4', '\xA4', '\xA6', '\xA4', '\xA8', '\xA4', '\xAA', '\x0a', '\0'}); // あいうえお
    printerCharacteristic->writeValue({'\xA3', '\xB1', '\xA3', '\xB2', '\xA3', '\xB3', '\xA3', '\xB4', '\xA3', '\xB5'});
    printerCharacteristic->writeValue({'\xA3', '\xB6', '\xA3', '\xB7', '\xA3', '\xB8', '\xA3', '\xB9', '\xA3', '\xB0', '\x0a', '\0'}); // 1234567890
    printerCharacteristic->writeValue(0x1c); // CHINESE MODE OFF
    printerCharacteristic->writeValue(0x2e);
}

/*
 * Test print
 */
void testPrint()
{
    for (int i = 0; i < 3; i++)
    {
        if (deviceBleConnected)
        {
            testCharacter();
            testJapanese();
            break;
        }
        connectToServer(peer_bd_addr);
        sleep(3);
    }
}

/*
 * Setup
 */
void setup()
{
    Serial.begin(115200);
    M5.begin();
    dacWrite(25, 0); // Speaker OFF

    Serial.begin(115200);
    M5.Lcd.setTextFont(2);
    M5.Lcd.println("BLE print test.");
    M5.Lcd.println();
    M5.Lcd.println("A button: scan BLE & log.");
    M5.Lcd.println("B button: test print.");

    BLEDevice::init("M5Stack");
}

/*
 * main loop
 */
void loop()
{
    if(M5.BtnA.wasPressed())
    {
        scan();
    }
    if(M5.BtnB.wasPressed())
    {
        testPrint();
    }
    M5.update();
}

ToDo

接続して、印刷できることが確認できました。ちゃんと使うには、

  • プリンター文字コードがGB18030という中国の文字コードなので、UTF-8からGB18030に変換してあげる必要がある
  • M5Stackに最適な文字コード変換ライブラリーが見つからない

などの課題がありそうです。最近文字コードを心配する機会が減ってきた気がしていたのですが。。。

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?