2015-08-22 13:41:00 u011046143 阅读数 3664
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3498 人正在学习 去看看 许英俊

IOS蓝牙小票打印

一、首先,蓝牙小票打印机,有自己的demoSDK

1.我这里的SDK ,主要有两个文件,一:libUartLib.a 二:UartLib.h 即封装好的静态库,和头文件

UartLib.h 代码:

#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
typedef  enum {
    DISCONNECT =0X00,
    CONNECTING,
    CONNECTED,
}ConnectStatus;

/***********************************
 Ble scan Delegate
 *************************************/
@protocol BleScanDelegate <NSObject>
- (void) peripheralItemRefresh;
- (void) peripheralStatePoweredOff;
@end
@protocol UartDelegate <NSObject>
/*!
 *  @method didBluetoothPoweredOff
 *
 *
 */
- (void) didBluetoothPoweredOff;
/*!
 *  @method didScanedPeripherals:(NSMutableArray  *)foundPeripherals
 *
 *  @param foundPeripherals           A <code>foundPeripherals</code> object.
 *
 *  @discussion                 This method is invoked while scanning, upon the discovery 
 *
 */
- (void) didScanedPeripherals:(NSMutableArray  *)foundPeripherals;

/*!
 *  @method didConnectPeripheral:(CBPeripheral *)peripheral
 *
 *  @param peripheral   The <code>CBPeripheral</code> that has connected.
 *
 *  @discussion         This method is invoked when a connection initiated by@link UartLib:ConnectPeripheral: @/link has succeeded.
 *
 */
- (void) didConnectPeripheral:(CBPeripheral *)peripheral;

/*!
 *  @method didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
 *
 *  @param peripheral   The <code>CBPeripheral</code> that has disconnected.
 *  @param error        If an error occurred, the cause of the failure.
 *
 *  @discussion         This method is invoked upon the disconnection of a peripheral that was connected by@link UartLib:ConnectPeripheral: @/link. If the disconnection
 *                      was not initiated by @link UartLib:disconnectPeripheral: @/link, the cause will be detailed in the <i>error</i> parameter. 
 *
 */
- (void) didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;

- (void) didReceiveData:(CBPeripheral *)peripheral recvData:(NSData *)recvData;

- (void) didWriteData:(CBPeripheral *)peripheral error:(NSError *)error;

- (void) didRecvRSSI:(CBPeripheral *)peripheral RSSI:(NSNumber *)RSSI;
@end

@interface UartLib :NSObject <BleScanDelegate>

@property (nonatomic,assign) id<UartDelegate> uartDelegate;

/*!
 *  @method scanStart
 *
 *  @discussion         Starts scanning for peripherals.
 *
 */
- (void) scanStart;

/*!
 *  @method scanStop
 *
 *  @discussion         Stops scanning for peripherals.
 *
 */
- (void) scanStop;

/*!
 *  @method connectPeripheral:(CBPeripheral*)peripheral
 *
 *  @param peripheral   The <code>CBPeripheral</code> to be connected.
 *
 *  @discussion         Initiates a connection to <i>peripheral</i>. Connection attempts never time out and, depending on the outcome, will result in a call to either@link UartLib:didConnectPeripheral: @/link or 
        @link UartLib:didDisconnectPeripheral:error: @/link.
 *                      Pending attempts are cancelled automatically upon deallocation of <i>peripheral</i>, and explicitly via@link cancelPeripheralConnection @/link.
 *
 *
 */
- (void) connectPeripheral:(CBPeripheral*)peripheral;

/*!
 *  @method disconnectPeripheral:(CBPeripheral*)peripheral
 *
 *  @param peripheral   A <code>CBPeripheral</code>.
 *
 *  @discussion         Cancels an active or pending connection to <i>peripheral</i>. Note that this is non-blocking, and any <code>CBPeripheral</code>
 *                      commands that are still pending to <i>peripheral</i> may or may not complete.
 *
 */
- (void) disconnectPeripheral:(CBPeripheral*)peripheral;

/*!
 *  @method sendValue:(CBPeripheral*)peripheral sendData:(NSData *)data type:(CBCharacteristicWriteType)type
 *
 *  @param peripheral   A <code>CBPeripheral</code>.
 *  @param data The value to write.
 *  @param type The type of write to be executed.
 *
 *  @discussion Writes <i>value</i> to <i>CBPeripheral</i>.
 *
 */
- (void)sendValue:(CBPeripheral*)peripheral sendData:(NSData *)data type:(CBCharacteristicWriteType)type;


/*!
 *  @method readRSSI:(CBPeripheral*)peripheral
 *
 *  @param peripheral   A <code>CBPeripheral</code>.
 *
 *  @discussion         discussion Retrieves the current RSSI of the link.
 *
 */
- (void) readRSSI:(CBPeripheral*)peripheral;
@end

2.主要用到 UartLib对象的代理方法,实现对蓝牙的搜索,发现,连接,断开等状态的返回。

二、代码的实现

1.静态库,和头文件引用

#import "UartLib.h"

2.声明UartLib对象,并设置代理

  self.mUartLib = [[UartLiballoc] init];
  [self.mUartLibsetUartDelegate:self];

这里UartLib对象不能定义成局部变量,否则会被立即释放,最好定义成成员变量;

3.实现代理方法

#pragma mark -
#pragma mark UartDelegate
                                                   /****************************************************************************/
    /*                       UartDelegate Methods                        */
    /****************************************************************************/
    -(void)didBluetoothPoweredOn
    {
        //当UartLib对象初始化后,代理设置完。如果设备蓝牙打开,会回调这个方法,说明设备蓝牙可以使用,
        //这里定义一个成员变量供外部访问,说明蓝牙开启。并且调用UartLib的开始搜索方法scanStart 搜索蓝牙打印机设备
        NSLog(@"didBluetoothPoweredOn");
        self.isBlueToothOn =YES;
        [self.mUartLibscanStart];
    }

    - (void) didBluetoothPoweredOff
    {
     //当设备蓝牙关闭,会调用这个方法,说明蓝牙关闭
    //这里声明了一个成员变量CBPeripheral(搜索到的蓝牙设备对象,每个蓝牙设备都对应一个CBPeripheral对象,是系统对象),供其他方法访问。这个对象存储设备正在连接的外部蓝牙设备。如果蓝牙关闭,我们要手动停止UartLib的搜索功能,(这里为了性能考虑)

    //当设备蓝牙关闭,会调用这个方法,说明蓝牙关闭
    //这里声明了一个成员变量CBPeripheral(搜索到的蓝牙设备对象,每个蓝牙设备都对应一个CBPeripheral对象,是系统对象),供其他方法访问。这个对象存储设备正在连接的外部蓝牙设备。如果蓝牙关闭,我们要手动停止UartLib的搜索功能,(这里为了性能考虑)
    NSLog(@"didBluetoothPoweredOff");
    self.isBlueToothOn =NO;
    self.mConnectPeripheral =nil;
    [self.mUartLib scanStop];
    }

    - (void) didScanedPeripherals:(NSMutableArray  *)foundPeripherals
    {
    //这个方法是调用频率,最高的方法。
    //UartLib对象每进行一次搜索都会调用这个方法,并将搜索到的设备以CBPeripheral对象,封装成数组放在foundPeripherals中,供我们使用。即使搜索到0个设备也会调用,并返回一个空数组。
    //数组中只会有蓝牙小票打印机的设备,其他蓝牙设备不会在数组中。所有不用担心过滤出蓝牙小票机的问题,方法内部已经过滤掉。
    //foundPeripherals每次都返回,都会将搜索到的设备封装在数组中,不会因为搜索过,就减少。
    //foundPeripherals会短时间内自动不搜索,上次断开的设备或连接的设备(在下面做处理的时候要考虑这种情况)。但过一会就会搜索到。
    //这里我们主要实现一个目的,将搜索到的设备存储到mPeripherals数组(这个数组用copy声明,这样就不会被foundPeripherals,影响)中供外部访问用。这里还不能将foundPeripherals这个数组直接返回给外部用,因为调用频率非常高,会直接导致内存过高,程序崩溃。这里我们要判断foundPeripherals数组中,与我们存储的设备mPeripherals进行比较,是否搜索到新的设备,是否有设备关闭等外部设备发生变化,相应地改变mPeripherals的设备。当mPeripherals数组发生变化,我们发出通知,告诉外部这个数组发生了变化,请刷新数据。这里进行了多次if判断,就是为了性能考虑。如果采用两次for循环也能判断出两个数组中有不同,但内存消耗会比较大。会崩。所以进行多次if判断,将一些情况过滤出来,再进行循环,这样性能会好很多
         NSLog(@"搜索到的设备数量(%zd)", [foundPeripherals count]);

       if (foundPeripherals.count <=0) {
       return;
    }

       if (self.mPeripherals.count <=0) {
       self.mPeripherals = foundPeripherals;

        //设备刷新,发出通知
        [[NSNotificationCenterdefaultCenter] postNotificationName:kNotificationBlueToothUpdateobject:nil];
       return;
    }

    if (self.mConnectPeripheral.state==CBPeripheralStateConnected) {
       // 已连接
       if (foundPeripherals.count!=(self.mPeripherals.count-1)) {
           NSPredicate* predicate = [NSPredicatepredicateWithFormat: @"identifier == %@", _mConnectPeripheral.identifier];
           NSArray* results = [foundPeripherals filteredArrayUsingPredicate: predicate];
           if (0==[resultscount]) {
                [foundPeripheralsaddObject:self.mConnectPeripheral];
            }
           self.mPeripherals = foundPeripherals;
            [[NSNotificationCenterdefaultCenter] postNotificationName:kNotificationBlueToothUpdateobject:nil];
           return;
        }
    }else {
       // 未连接
       if (foundPeripherals.count!=self.mPeripherals.count) {
           self.mPeripherals = foundPeripherals;
            [[NSNotificationCenterdefaultCenter] postNotificationName:kNotificationBlueToothUpdateobject:nil];
           return;
        }
    }

       for (id itemin foundPeripherals) {
       if ([item isKindOfClass:[CBPeripheralclass]]) {
           CBPeripheral* tCBPeripheral = (CBPeripheral*)item;

           NSPredicate* predicate = [NSPredicatepredicateWithFormat: @"identifier == %@", tCBPeripheral.identifier];
           NSArray* results = [_mPeripheralsfilteredArrayUsingPredicate: predicate];
           if (0==[resultscount]) {
                if (self.mConnectPeripheral.state==CBPeripheralStateConnected) {
                   // 已连接
                   NSPredicate* predicate = [NSPredicatepredicateWithFormat: @"identifier == %@", _mConnectPeripheral.identifier];
                   NSArray* results = [foundPeripherals filteredArrayUsingPredicate: predicate];
                   if (0==[resultscount]) {
                        [foundPeripheralsaddObject:self.mConnectPeripheral];
                    }
                   self.mPeripherals = foundPeripherals;
                }else {
                   // 未连接
                   self.mPeripherals = foundPeripherals;
                }
                [[NSNotificationCenterdefaultCenter] postNotificationName:kNotificationBlueToothUpdateobject:nil];
               break;
               return;
            }
        }
    }
    }

    - (void) didConnectPeripheral:(CBPeripheral *)peripheral
    {
    //设备连接成功会调用这个方法,这是将这个设备赋值给self.mConnectPeripheral对象。并发出连接成功通知
       NSLog(@"didConnectPeripheral");
       NSLog(@"链接成功");
       self.mConnectPeripheral = peripheral;
    [[NSNotificationCenterdefaultCenter] postNotificationName:kNotificationBlueToothConnectSuccessobject:nil];
    }

    - (void) didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
    {
    //设备断开会调用这个方法,并发出断开通知
    NSLog(@"didDisconnectPeripheral");
       if (error == nil) {
       NSLog(@"断开成功");
        [[NSNotificationCenterdefaultCenter]postNotificationName:kNotificationBlueToothDisConnectSuccessobject:nil];
    }else {
        [[NSNotificationCenterdefaultCenter]postNotificationName:kNotificationBlueToothDisConnectFailureobject:nil];
    }
    }

    - (void) didReceiveData:(CBPeripheral *)peripheral recvData:(NSData *)recvData
    {
    }

    - (void) didWriteData:(CBPeripheral *)peripheral error:(NSError *)error
    {
    }

    - (void) didRecvRSSI:(CBPeripheral *)peripheral RSSI:(NSNumber *)RSSI
    {
    }

    - (void) didRetrievePeripheral:(NSArray *)peripherals
    {
    }

    - (void)didDiscoverPeripheral:(CBPeripheral *)peripheral RSSI:(NSNumber *)RSSI
    {
    }

这里我主要用到5个代理方法
因为整个应用程序不止一个地方需要用到打印,所以UartLib对象我定义在AppDelegate中,即变成单例的对象,让整个应用程序可以访问打印。

4.连接设备

前面都完成了,这里就简单了,只需要获取self.mPeripherals这里的设备,找到我们要得设备,只需要调用
[mAppDelegate.mUartLibconnectPeripheral:tPeripheral];
UartLib的连接设备方法,即可。

5.打印内容

这个这里就不粘代码了,demo中有方法,将字符串传进去,会生成指令然后放到一个数组中self.mSendDataArray。这里如果有懂指令的,可以改他们的指令,生成格式更复杂的打印效果。这里我只用他们了给的指令。

6.打印

这里打印需要设置一个定时器,不断的执行一个打印方法,将指令一条一条送到打印机中,打印出来。

self.timer=[NSTimerscheduledTimerWithTimeInterval:(float)0.02target:selfselector:@selector(sendDataTimer:) userInfo:nilrepeats:YES];
- (void) sendDataTimer:(NSTimer *)timer {
NSLog(@"执行定时器..");
   if ([self.mSendDataArraycount] > 0) {
   NSData* cmdData;
   cmdData = [self.mSendDataArrayobjectAtIndex:0];
   if (self.mUartLib) {
            [self.mUartLibsendValue:self.mConnectPeripheralsendData:cmdDatatype:CBCharacteristicWriteWithResponse];
        [self.mSendDataArrayremoveObjectAtIndex:0];
   }
   }
}
2016-03-03 18:02:51 arvinlqj 阅读数 12960
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3498 人正在学习 去看看 许英俊

前段时间做了一个收银的项目,里面要用到链接小票打印机打印小票的功能,牵涉到ios的蓝牙链接,打印指令等方面内容,这里总结一些使用心得。

关于ios系统的蓝牙,现在的ios支持的是蓝牙4.0,一般意义的蓝牙教程也都是关于蓝牙4.0的了。常规的操作这里不进行介绍,给大家推荐一个开源的iOS蓝牙项目,BabyBluetooth ,BabyBluetooth极好的封装了CoreBluetooth,可以使你更简单的使用CoreBluetooth的API,另外BabyBluetooth采用链式编程,是代码更加简洁优雅。有兴趣的同学可以研究下。

   至于打印这块是采用的ESC/POS指令来控制打印机的行为。当然如果你就是使用的EPSON打印机,那么肯定会有相应的SDK的,就省去了使用最原始ESC/POS指令打印的繁琐。ESC/POS指令集网上有很多,自己搜索一些就好了。不过可能有的朋友看了指令集之后仍然不会使用,这里贴一下常用的控制指令:

/**
 *  重置打印机
 */
- (void)resetPrinter
{
    Byte reset[] = {0x1B,0X40};
    [self writeData:[NSData dataWithBytes:reset length:2]];
}

/**
 *  唤醒打印机
 */
- (void)wakeUpPrinter
{
    Byte wake[] = {0x00};
    
    [self writeData:[NSData dataWithBytes:wake length:1]];
}

/**
 *  打印并换行
 */
- (void)printAndGoToNextLine
{
    Byte next[] = {0x0A};
    
    [self writeData:[NSData dataWithBytes:next length:1]];
}


- (void)printAndEnter
{
    Byte enter[] = {0x0D};
    
    [self writeData:[NSData dataWithBytes:enter length:1]];
}

/**
 *  加大字体
 *
 *  @param n 位宽,位高
 */
- (void)enlargeFont:(int )n
{
    Byte font[] = {0x1D,0X21,n};
    
    [self writeData:[NSData dataWithBytes:font length:3]];
}

/**
 *  设置对齐方式
 *
 *  @param n 0左对齐,1中对齐,2右对齐
 */
- (void)setAlignment:(int)n
{
    Byte align[] = {0x1B,0x61,n};

    [self writeData:[NSData dataWithBytes:align length:3]];
}

/**
 *  打印缓冲区数据并进纸n个垂直点距,0<= n <= 255,一个垂直点距为0.125mm
 *
 *  @param n
 */
- (void)printAndGoNLine:(int)n
{
    Byte line[] = {0x1B,0X64,n};
    
    [self writeData:[NSData dataWithBytes:line length:3]];
}

/**
 *  结束打印
 */
- (void)printEnd
{
    Byte end[] = {0x1d, 0x4c, 0x1f, 0x00};
    [self writeData:[NSData dataWithBytes:end length:4]];
}

/**
 *  写入数据
 *
 *  @param param 可以是NSData,也可以是NSString
 */
- (void)writeData:(id )param
{
    NSData *data = nil;
    
    if ([param isKindOfClass:[NSString class]]) {
        NSString *str = (NSString *)param;
        data = [str dataUsingEncoding:CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000)];
    }else if ([param isKindOfClass:[NSData class]]){
        data = (NSData *)param;
    }
    
    if ([self setUp]) {
        [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
    }
}

具体的打印效果和格式就使用这些基本指令来组合就好了。这里提醒大家一下,一定要看好你的打印机支持的语言,以便于在打印的时候进行相应的encode。当然你可以根据指令来进行其他效果的组合,上面贴出的是打印文字时候常用的指令组合。如果是做的收银小票的打印,那么在商品列表那里应该是两端对齐的格式。在博主的试验中实时动态的对打印机设置对齐方式是不起作用的,或许是方法不对,如果有大神可以实现请千万不吝赐教。博主采用的方法是在两端文字中间动态的拼接空格来达到两端对齐的目的,那么这里的知识点就来了,那就是计算字符串的长度,有同学可能觉得字符串长度ios不是提供了length了吗?如果你的打印效果是同一种类型的文字当然可以,但如果同时包含字母,汉子,数字等,那么用length计算的长度就不准确了,因为字母,汉子的编码不同,字节数是不同的,所以要转换成Unicode编码统一的计算。下面是博主使用的计算方法:

//判断中英混合的的字符串长度
- (int)convertToInt:(NSString*)strtemp
{
    int strlength = 0;
    char* p = (char*)[strtemp cStringUsingEncoding:NSUnicodeStringEncoding];
    for (int i=0 ; i<[strtemp lengthOfBytesUsingEncoding:NSUnicodeStringEncoding] ;i++) {
        if (*p) {
            p++;
            strlength++;
        }
        else {
            p++;
        }
        
    }
    return strlength;
}

既然已经计算出了字符串的长度,那么动态的拼接空格的操作就简单了:

/**
 *  动态拼接空格使所有字符串长度一致,比如:(辣条x2     ¥100)
 *
 *  @param leader 字符串的左边部分,比如:辣条x2
 *  @param tail   字符串的右边部分,比如:¥100
 *
 *  @return 拼接空格之后的字符串
 */
- (NSString *)getPrintString:(NSString *)leader tail:(NSString *)tail
{
    static int TOTAL = 30;//这里是根据你的纸张宽度试验出来的一个合适的总字数
    
    NSMutableString *printString = [NSMutableString new];
    [printString appendString:leader];
    
    int lenderLen = [self convertToInt:leader];
    
    if (tail) {
        int tailLen = [self convertToInt:tail];
        int detal = (int)(TOTAL - lenderLen - tailLen);
        for (int i = 0; i < detal; i ++) {
            [printString appendString:@" "];
        }
        [printString appendString:tail];
    }else{
        int detal = (int)(TOTAL - lenderLen);
        for (int i = 0; i < detal; i ++) {
            [printString appendString:@" "];
        }
    }
    
    return printString;
}


但是大家在现实中一定有这样的需求,那就是在票据的最下方需要打印二维码。这里就涉及到了图片的打印,说来这里有一个小插曲,本人在做这个项目的时候对于图片的打印百般调试都达不到预定效果,而网上也没找到理想的材料,后来论坛发现有人做过,就抱着求教的心态去请教,人家是爽快人,果断说2000块钱,代码卖你。当时博主什么都没说,默默的删掉了他的联系方式。但是其实内心对这种没有分享精神的人是比较鄙视的。这里说一下自己的研究成果。如果你研究了ESC/POS指令集的话会发现其中有图片的打印指令,而图片的打印也没有那么神秘,只不过是把你的图片转换一下点阵图,然后按照指令打印就OK了:

-(DXImage *)getBitmapImageData{
    CGImageRef cgImage = [m_image CGImage];
	int32_t width = CGImageGetWidth(cgImage);
	int32_t height = CGImageGetHeight(cgImage);
    NSInteger psize = sizeof(ARGBPixel);
	ARGBPixel * pixels = malloc(width * height * psize);
    NSMutableData* data = [[NSMutableData alloc] init];
    [self ManipulateImagePixelDataWithCGImageRef:cgImage imageData:pixels];
    for (int h = 0; h < height; h++) {
        for (int w = 0; w < width; w++) {
            int pIndex = [self PixelIndexWithX:w y:h width:width];
            ARGBPixel pixel = pixels[pIndex];
            if ([self PixelBrightnessWithRed:pixel.red green:pixel.green blue:pixel.blue] <= 127) {
                u_int8_t ch = 0x01;
                [data appendBytes:&ch length:1];
            }
            else{
                 u_int8_t ch = 0x00;
                [data appendBytes:&ch length:1];
            }
        }
    }
    DXImage* bi = [[DXImage alloc] init];
    bi.bitmap = data;
    bi.width = width;
    bi.height = height;
    return bi;
}

-(NSData *)getDataForPrint{
    DXImage* bi = [self getBitmapImageData];
    const char* bytes = bi.bitmap.bytes;
    NSMutableData* dd = [[NSMutableData alloc] init];
    //横向点数计算需要除以8
    NSInteger w8 = bi.width / 8;
    //如果有余数,点数+1
    NSInteger remain8 = bi.width % 8;
    if (remain8 > 0) {
        w8 = w8 + 1;
    }
    /**
    根据公式计算出 打印指令需要的参数
    指令:十六进制码 1D 76 30 m xL xH yL yH d1...dk
     m为模式,如果是58毫秒打印机,m=1即可
     xL 为宽度/256的余数,由于横向点数计算为像素数/8,因此需要 xL = width/(8*256)
     xH 为宽度/256的整数
     yL 为高度/256的余数
     yH 为高度/256的整数
    **/
    NSInteger xL = w8 % 256;
    NSInteger xH = bi.width / (8 * 256);
    NSInteger yL = bi.height % 256;
    NSInteger yH = bi.height / 256;
    
    const char cmd[] = {0x1d,0x76,0x30,0,xL,xH,yL,yH};
    [dd appendBytes:cmd length:8];
    
    for (int h = 0; h < bi.height; h++) {
        for (int w = 0; w < w8; w++) {
            u_int8_t n = 0;
            for (int i=0; i<8; i++) {
                int x = i + w * 8;
                u_int8_t ch;
                if (x < bi.width) {
                    int pindex = h * bi.width + x;
                    ch = bytes[pindex];
                }
                else{
                    ch = 0x00;
                }
                n = n << 1;
                n = n | ch;
            }
            [dd appendBytes:&n length:1];
        }
    }
    return dd;
}
</pre><pre name="code" class="objc"><pre name="code" class="objc">-(void)ManipulateImagePixelDataWithCGImageRef:(CGImageRef)inImage imageData:(void*)oimageData
{
    // Create the bitmap context
    CGContextRef cgctx = [self CreateARGBBitmapContextWithCGImageRef:inImage];
    if (cgctx == NULL)
    {
        // error creating context
        return;
    }
	
	// Get image width, height. We'll use the entire image.
    size_t w = CGImageGetWidth(inImage);
    size_t h = CGImageGetHeight(inImage);
    CGRect rect = {{0,0},{w,h}};
	
    // Draw the image to the bitmap context. Once we draw, the memory
    // allocated for the context for rendering will then contain the
    // raw image data in the specified color space.
    CGContextDrawImage(cgctx, rect, inImage);
	
    // Now we can get a pointer to the image data associated with the bitmap
    // context.
    void *data = CGBitmapContextGetData(cgctx);
    if (data != NULL)
    {
		CGContextRelease(cgctx);
		memcpy(oimageData, data, w * h * sizeof(u_int8_t) * 4);
		free(data);
		return;
    }
	
    // When finished, release the context
    CGContextRelease(cgctx);
    // Free image data memory for the context
    if (data)
    {
        free(data);
    }
    
	return;
}

// 参考 http://developer.apple.com/library/mac/#qa/qa1509/_index.html
-(CGContextRef)CreateARGBBitmapContextWithCGImageRef:(CGImageRef)inImage
{
    CGContextRef    context = NULL;
    CGColorSpaceRef colorSpace;
    void *          bitmapData;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;
	
	// Get image width, height. We'll use the entire image.
    size_t pixelsWide = CGImageGetWidth(inImage);
    size_t pixelsHigh = CGImageGetHeight(inImage);
	
    // Declare the number of bytes per row. Each pixel in the bitmap in this
    // example is represented by 4 bytes; 8 bits each of red, green, blue, and
    // alpha.
    bitmapBytesPerRow   = (pixelsWide * 4);
    bitmapByteCount     = (bitmapBytesPerRow * pixelsHigh);
	
    // Use the generic RGB color space.
    colorSpace =CGColorSpaceCreateDeviceRGB();
    if (colorSpace == NULL)
    {
        return NULL;
    }
	
    // Allocate memory for image data. This is the destination in memory
    // where any drawing to the bitmap context will be rendered.
    bitmapData = malloc( bitmapByteCount );
    if (bitmapData == NULL)
    {
        CGColorSpaceRelease( colorSpace );
        return NULL;
    }
	
    // Create the bitmap context. We want pre-multiplied ARGB, 8-bits
    // per component. Regardless of what the source image format is
    // (CMYK, Grayscale, and so on) it will be converted over to the format
    // specified here by CGBitmapContextCreate.
    context = CGBitmapContextCreate (bitmapData,
									 pixelsWide,
									 pixelsHigh,
									 8,      // bits per component
									 bitmapBytesPerRow,
									 colorSpace,
									 kCGImageAlphaPremultipliedFirst);
    if (context == NULL)
    {
        free (bitmapData);
    }
	
    // Make sure and release colorspace before returning
    CGColorSpaceRelease( colorSpace );
	
    return context;
}

-(u_int8_t)PixelBrightnessWithRed:(u_int8_t)red green:(u_int8_t)green blue:(u_int8_t)blue
{
    int level = ((int)red + (int)green + (int)blue)/3;
	return level;
}

-(u_int32_t)PixelIndexWithX:(u_int32_t)x y:(u_int32_t)y width:(u_int32_t)width
{
    return (x + (y * width));
}

-(UIImage*)ScaleImageWithImage:(UIImage*)image width:(NSInteger)width height:(NSInteger)height
{
    CGSize size;
	size.width = width;
	size.height = height;
    UIGraphicsBeginImageContext(size);
    [image drawInRect:CGRectMake(0, 0, width, height)];
    UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return scaledImage;
}

-(NSInteger)GetGreyLevelWithARGBPixel:(ARGBPixel)source intensity:(float)intensity
{
    if (source.alpha == 0)
	{
		return 255;
	}
	
	int32_t gray = (int)(((source.red + source.green +  source.blue) / 3) * intensity);
	
	if (gray > 255)
		gray = 255;
	
	return (u_int8_t)gray;
}



把你的图片转换之后就可以使用上面的writeData方法来打印图片了。

2016-08-02 15:59:27 yuanpeng1014 阅读数 398
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3498 人正在学习 去看看 许英俊
蓝牙打印的几个主要步骤:
1.扫描蓝牙 (打印机有蓝牙功能并开启)
- (IBAction)scanlPrint:(UIButton *)sender {
    
    [uartLib scanStart];
    
}
2.连接蓝牙 (停止扫描)
- (IBAction)connectPrint:(UIButton *)sender {
    
    NSLog(@"connect Peripheral");
    [uartLib scanStop];
    [uartLib connectPeripheral:connectPeripheral];
    
    [connectAlertView show];
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(printInput) name:@"printBill" object:nil];


}
3.打印 (格式不易控制)
- (void)printInput{
    NSString *curPrintContent;
    
    curPrintContent = [sendDataView text];
    
    NSString * curPrintContent = @"打印测试这里有好长一段话,还是看不到呀,要不然我看不到,这里是什么格式";
        if ([curPrintContent length]) {
            NSString *printed = [curPrintContent stringByAppendingFormat:@"%c%c%c", '\n', '\n', '\n'];
            [self PrintWithFormat:printed];
    }
}


用了UartLib的SDK
所以要导入UartLib的头文件,设置delegate,代理里面的方法必须实现,还有加入必要的成员变量

2017-06-22 17:54:27 i_k_o_x_s 阅读数 1263
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3498 人正在学习 去看看 许英俊

1. OAD升级机制

OAD升级有两个Bin格式文件  Image-A 和 Image-B 

为了防止蓝牙升级

当在升级的时候,为了防止蓝牙升级出错,需要先查询当前蓝牙镜像 是 Image-A 还是 Image-B 

如果当前是 A 就取 B文件去升级,否则 是B 就取 A文件去升级
    FFC1 特征值用来发送查询版本信息  和 发送升级通知的蓝牙信息

分别发送 0  、1 给蓝牙设备
返回设备镜像信息如:    <0204007c 42424242>     

关于OAD运行机制 参考: http://blog.csdn.net/mirkerson/article/details/39007591


2.蓝牙镜像 类型 版本 大小查询

查询镜像信息的特征  Characteristic

#define BT_OAD_IMAGE_NOTIFY@"F000FFC1-0451-4000-B000-000000000000"    

#define BT_OAD_IMAGE_BLOCK_REQUEST @"F000FFC2-0451-4000-B000-000000000000"

    //FFC2 特征值用来传输升级信息

    CBPeripheral *peripheral;

    CBCharacteristic *characteristic_oad_1; 

    CBCharacteristic *characteristic_oad_2;

    //蓝牙设备所以有特征值

    NSDictionary *dic = [[BlueToothManagershareBluetoothManager]connectDic];

    characteristic_oad_1 = [dicobjectForKey:BT_OAD_IMAGE_NOTIFY];

    characteristic_oad_2 = [dicobjectForKey:BT_OAD_IMAGE_BLOCK_REQUEST];

    [peripheralsetNotifyValue:YESforCharacteristic:characteristic_oad_1];

    [peripheralsetNotifyValue:YESforCharacteristic:characteristic_oad_2];

3.发送查询蓝牙设备信息

    Byte byte0[] = {0};

     NSData *data0 = [NSDatadataWithBytes:byte0length:sizeof(byte0)];

   [peripheralwriteValue:data0forCharacteristic:characteristic_oad_1type:CBCharacteristicWriteWithResponse];

   sleep(1);

     Byte byte1[] = {1};

     NSData *data1 = [NSDatadataWithBytes:byte1length:sizeof(byte1)];

 [peripheralwriteValue:data1forCharacteristic:characteristic_oad_1type:CBCharacteristicWriteWithResponse];


可写(01)、监听(返回8bytes数据)。

当写入0时若有8bytes数据返回,则说明当前硬件固件为镜像A,若写入0无返回数据,而写入18bytes数据返回,则说明当前硬件固件为镜像B

Byte 0 和 Byte 1 表示 镜像的编译版本号    高低位要对调   应该为:  0402 =>  4*16*16 + 2  = 1026   

根据嵌入式对应的 应该再除以2  为编译版本号为513   (具体未去探究,知道的朋友告诉我下,暂时没明白。)

Byte 2 和 Byte 3 表示 镜像的大小        高低位要对调计算  应该为 :   7c00 => 7*16*16*16 + 12*16*16 = 31744 B 

        根据几个实际的bin文件 应该是 124KB  。    猜想应该是  31744 * 4 = 126976 B = 124 KB 。(知道的朋友告诉我下,暂时没明白。)

Byte 4 - Byte 7   表示  当前是B镜像    默认情况下,镜像A中这4bytes均为AASCII码(即40x41),而镜像B中均为BASCII码(即40x42)。


4.发送升级信息

根据3可以得知 设备是镜像A还是B 。

选择对应Bin文件 发送升级信息

            NSString *filePathA = [[NSBundlemainBundle]pathForResource:@"IMAGE_A(8)"ofType:@"bin"];    

            oadData = [NSDatadataWithContentsOfFile:filePathA];

            Byte *Dvalue = (Byte *)[oadDatabytes];    

            NSData *data = [oadDatasubdataWithRange:NSMakeRange(4,8)];

            NSData *sendData = [NSDatadataWithBytes:bytelength:sizeof(byte)];

            [peripheralwriteValue:sendDataforCharacteristic:characteristic_oad_1type:CBCharacteristicWriteWithResponse];

这里的截取oadData  4 —— 12    8个字节的信息 实际上是要升级包的包信息  发送给设备

比如要发送   A镜像 -> 发送  <0100007c 41414141> 

在CBPeripheralDelegate

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullableNSError *)error

{

         // 如果是返回的UUIDString 是升级的特征值 表示可以升级

         if ([characteristic.UUID.UUIDStringisEqualToString:BT_OAD_IMAGE_BLOCK_REQUEST])

        {

               //启动定时器发送 升级文件 网上看了一些Demo说是 可以取消对这个特征值的监听 ()

                [peripheralsetNotifyValue:NOforCharacteristic:characteristic];

                [selfstartSendData];

         }

}

5.分包发送升级文件

- (void)startSendData

{

   if (!sendTime &&allowUpdate

   {

       x =0;

// 这里以每秒 20ms 发送一个包  在iOS 20ms 没问题。  在安卓中 大于 50ms, 已经避免蓝牙拥堵造成升级失败。

        sendTime[NSTimerscheduledTimerWithTimeInterval:0.02target:selfselector:@selector(sendData:)userInfo:nilrepeats:YES];

   }

}



- (void)sendData:(NSTimer *)t{

    

        //  把Bin文件 转换成NSData  分包传输  每个包 为18个字节 

        //  前2个字节 表示包的索引  第几个Block    后16个字节表示 内容  。  固定

    Byte *Dvalue = (Byte *)[oadDatabytes];

    Byte byte[18];

    for (int i =0; i < 18; i++) {

        if (i <=1 ) {

            byte[0] =x%256;

            byte[1] =x/256;

        }

        else{

            if (i -2 + x*16 <oadData.length) {

                byte[i] = Dvalue[i -2 + x*16];

            }

            else{

                byte[i] =0xFF;

            }

        }

    }

    

    NSData *sendData = [NSDatadataWithBytes:byte length:sizeof(byte)];

    NSLog(@"\n%ld - \n%@",x,sendData);

    [peripheralwriteValue:sendData forCharacteristic:characteristic_oad_2type:CBCharacteristicWriteWithoutResponse];

    x++;

    

    float sendValue = (float)x*16/(float)oadData.length;

    NSLog(@"升级了 %.2f",x,sendValue);


  // 此时设备蓝牙会断开

    if (x*16 >=oadData.length) {

        [sendTimeinvalidate];

        NSLog(@"升级完毕");

    }

}



关于iOS蓝牙打印机

阅读数 544

蓝牙

博文 来自: D_Tmac

今日分享-ios蓝牙

阅读数 882

iOS蓝牙Ble开发

阅读数 4206

iOS 蓝牙打印小票机

阅读数 1117

没有更多推荐了,返回首页