Skip to content

AT指令发送PDU短信详解

摘录,待验证:

本文以一个实例来解说AT指令发送PDU短信的全过程,假如我要发送下面的短信:
接收号码:+8613602433649
短信内容:工作愉快!
短信中心号码:+8613800200500

一、短信中心号码处理:用字符串 addr 表示
1、将短信息中心号码去掉+号,看看长度是否为偶数,如果不是,最后添加F
即 addr = “+8613800200500″
=> addr = “8613800200500F”
2、将奇数位和偶数位交换。
=> addr = “683108200005F0″
3、将短信息中心号码前面加上字符91,91是国际化的意思
=> addr = “91683108200005F0″
4、算出 addr 长度,结果除2,格式化成2位的16进制字符串,16 / 2 = 8 => “08″
=> addr = “0891683108200005F0″

二、手机号码处理:用字符串 phone
1、将手机号码去掉+号,看看长度是否为偶数,如果不是,最后添加F
即 phone = “+8613602433649″
=> phone = “8613602433649F”
2、将手机号码奇数位和偶数位交换。
=> phone = “683106423346F9″

三、短信息部分处理:用字符串 msg 表示
1、转字符串转换为Unicode代码,例如“工作愉快!”的unicode代码为 5DE54F5C61095FEBFF01,
(转换函数见最后附录)
2、将 msg 长度除2,保留两位16进制数,即 5DE54F5C61095FEBFF01 = 20 / 2 => “0A”,再加上 msg
=> msg = “0A5DE54F5C61095FEBFF01″

四、组合
1、手机号码前加上字符串 11000D91(1100:固定,0D:手机号码的长度,不算+号,十六进制表示,91:发送到手机为91,发送到小灵通为81),
即 phone = “11000D91″ + phone
=> 11000D91683106423346F9
2、手机号码后加上 000800 和刚才的短信息内容,000800也写死就可以了
即 phone = phone + “000800″ + msg
即 11000D91683106423346F9 + 000800 + 0A5DE54F5C61095FEBFF01
=> phone = 11000D91683106423346F90008000A5DE54F5C61095FEBFF01
3、phone 长度除以2,格式化成2位的十进制数
即 11000D91683106423346F90008000A5DE54F5C61095FEBFF01 => 50位 / 2 => 25

五、所以要发送的内容为
AT+CMGF=0 <回车>
OK
AT+CMGS=25<回车>
> addr+phone <Ctrl+Z发送>

六、如果返回不是ERROR,恭喜你,发送成功了^_^
//—————————————————–
// 7bit编码
// 输入: pSrc – 源字符串指针
//        nSrcLength – 源字符串长度
// 输出: pDst – 目标编码串指针
// 返回: 目标编码串长度
int gsmEncode7bit(const char* pSrc, unsigned char* pDst, int nSrcLength)
{
int nSrc;   // 源字符串的计数值
int nDst;   // 目标编码串的计数值
int nChar;   // 当前正在处理的组内字符字节的序号,范围是0-7
unsigned char nLeft; // 上一字节残余的数据
// 计数值初始化
nSrc = 0;
nDst = 0;
// 将源串每8个字节分为一组,压缩成7个字节
// 循环该处理过程,直至源串被处理完
// 如果分组不到8字节,也能正确处理
while (nSrc < nSrcLength)
{
// 取源字符串的计数值的最低3位
nChar = nSrc & 7;
// 处理源串的每个字节
if(nChar == 0)
{
// 组内第一个字节,只是保存起来,待处理下一个字节时使用
nLeft = *pSrc;
}
else
{
// 组内其它字节,将其右边部分与残余数据相加,得到一个目标编码字节
*pDst = (*pSrc << (8-nChar)) | nLeft;
// 将该字节剩下的左边部分,作为残余数据保存起来
nLeft = *pSrc >> nChar;
// 修改目标串的指针和计数值
pDst++;
nDst++;
}
// 修改源串的指针和计数值
pSrc++;
nSrc++;
}
// 返回目标串长度
return nDst;
}
// 7bit解码
// 输入: pSrc – 源编码串指针
//        nSrcLength – 源编码串长度
// 输出: pDst – 目标字符串指针
// 返回: 目标字符串长度
int gsmDecode7bit(const unsigned char* pSrc, char* pDst, int nSrcLength)
{
int nSrc;   // 源字符串的计数值
int nDst;   // 目标解码串的计数值
int nByte;   // 当前正在处理的组内字节的序号,范围是0-6
unsigned char nLeft; // 上一字节残余的数据
// 计数值初始化
nSrc = 0;
nDst = 0;

// 组内字节序号和残余数据初始化
nByte = 0;
nLeft = 0;
// 将源数据每7个字节分为一组,解压缩成8个字节
// 循环该处理过程,直至源数据被处理完
// 如果分组不到7字节,也能正确处理
while(nSrc<nSrcLength)
{
// 将源字节右边部分与残余数据相加,去掉最高位,得到一个目标解码字节
*pDst = ((*pSrc << nByte) | nLeft) & 0x7f;
// 将该字节剩下的左边部分,作为残余数据保存起来
nLeft = *pSrc >> (7-nByte);
// 修改目标串的指针和计数值
pDst++;
nDst++;
// 修改字节计数值
nByte++;
// 到了一组的最后一个字节
if(nByte == 7)
{
// 额外得到一个目标解码字节
*pDst = nLeft;
// 修改目标串的指针和计数值
pDst++;
nDst++;
// 组内字节序号和残余数据初始化
nByte = 0;
nLeft = 0;
}
// 修改源串的指针和计数值
pSrc++;
nSrc++;
}
// 输出字符串加个结束符
*pDst = ‘\0′;
// 返回目标串长度
return nDst;
}

// 正常顺序的字符串转换为两两颠倒的字符串,若长度为奇数,补’F'凑成偶数
// 如:”8613851872468″ –> “683158812764F8″
// 输入: pSrc – 源字符串指针
//        nSrcLength – 源字符串长度
// 输出: pDst – 目标字符串指针
// 返回: 目标字符串长度
int gsmInvertNumbers(const char* pSrc, char* pDst, int nSrcLength)
{
int nDstLength;   // 目标字符串长度
char ch;    // 用于保存一个字符
// 复制串长度
nDstLength = nSrcLength;
// 两两颠倒
for(int i=0; i<nSrcLength;i+=2)
{
ch = *pSrc++;   // 保存先出现的字符
*pDst++ = *pSrc++; // 复制后出现的字符
*pDst++ = ch;   // 复制先出现的字符
}
// 源串长度是奇数吗?
if(nSrcLength & 1)
{
*(pDst-2) = ‘F’; // 补’F’
nDstLength++;   // 目标串长度加1
}
// 输出字符串加个结束符
*pDst = ‘\0′;
// 返回目标字符串长度
return nDstLength;
}

// PDU编码,用于编制、发送短消息
// 输入: pSrc – 源PDU参数指针
// 输出: pDst – 目标PDU串指针
// 返回: 目标PDU串长度
int gsmEncodePdu(const SM_PARAM* pSrc, char* pDst)
{
int nLength;    // 内部用的串长度
int nDstLength;    // 目标PDU串长度
unsigned char buf[256]; // 内部用的缓冲区
// SMSC地址信息段
nLength = strlen(pSrc->SCA); // SMSC地址字符串的长度
buf[0] = (char)((nLength & 1) == 0 ? nLength : nLength + 1) / 2 + 1; // SMSC地址信息长度
buf[1] = 0×91;   // 固定: 用国际格式号码
nDstLength = gsmBytes2String(buf, pDst, 2);   // 转换2个字节到目标PDU串
nDstLength += gsmInvertNumbers(pSrc->SCA, &pDst[nDstLength], nLength); // 转换SMSC号码到目标PDU串
// TPDU段基本参数、目标地址等
nLength = strlen(pSrc->TPA); // TP-DA地址字符串的长度
buf[0] = 0×11;      // 是发送短信(TP-MTI=01),TP-VP用相对格式(TP-VPF=10)
buf[1] = 0;       // TP-MR=0
buf[2] = (char)nLength;    // 目标地址数字个数(TP-DA地址字符串真实长度)
buf[3] = 0×91;      // 固定: 用国际格式号码
nDstLength += gsmBytes2String(buf, &pDst[nDstLength], 4);   // 转换4个字节到目标PDU串
nDstLength += gsmInvertNumbers(pSrc->TPA, &pDst[nDstLength], nLength); // 转换TP-DA到目标PDU串
// TPDU段协议标识、编码方式、用户信息等
nLength = strlen(pSrc->TP_UD); // 用户信息字符串的长度
buf[0] = pSrc->TP_PID;    // 协议标识(TP-PID)
buf[1] = pSrc->TP_DCS;    // 用户信息编码方式(TP-DCS)
buf[2] = 0;       // 有效期(TP-VP)为5分钟
if(pSrc->TP_DCS == GSM_7BIT)
{
// 7-bit编码方式
buf[3] = nLength;    // 编码前长度
nLength = gsmEncode7bit(pSrc->TP_UD, &buf[4], nLength+1) + 4; // 转换TP-DA到目标PDU串
}
else if(pSrc->TP_DCS == GSM_UCS2)
{
// UCS2编码方式
buf[3] = gsmEncodeUcs2(pSrc->TP_UD, &buf[4], nLength); // 转换TP-DA到目标PDU串
nLength = buf[3] + 4;   // nLength等于该段数据长度
}
else
{
// 8-bit编码方式
buf[3] = gsmEncode8bit(pSrc->TP_UD, &buf[4], nLength); // 转换TP-DA到目标PDU串
nLength = buf[3] + 4;   // nLength等于该段数据长度
}
nDstLength += gsmBytes2String(buf, &pDst[nDstLength], nLength);   // 转换该段数据到目标PDU串
// 返回目标字符串长度
return nDstLength;
}

// 初始化GSM状态
BOOL gsmInit()
{
char ans[128];   // 应答串
// 测试GSM-MODEM的存在性
WriteComm(“AT\r”, 3);
ReadComm(ans, 128);
if (strstr(ans, “OK”) == NULL)   return FALSE;
// ECHO OFF
WriteComm(“ATE0\r”, 5);
ReadComm(ans, 128);
// PDU模式
WriteComm(“AT+CMGF=0\r”, 10);
ReadComm(ans, 128);
return TRUE;
}
// 发送短消息,仅发送命令,不读取应答
// 输入: pSrc – 源PDU参数指针
int gsmSendMessage(SM_PARAM* pSrc)
{
int nPduLength;   // PDU串长度
unsigned char nSmscLength; // SMSC串长度
int nLength;   // 串口收到的数据长度
char cmd[16];   // 命令串
char pdu[512];   // PDU串
char ans[128];   // 应答串
nPduLength = gsmEncodePdu(pSrc, pdu); // 根据PDU参数,编码PDU串
strcat(pdu, “\x01a”);   // 以Ctrl-Z结束
gsmString2Bytes(pdu, &nSmscLength, 2); // 取PDU串中的SMSC信息长度
nSmscLength++;   // 加上长度字节本身
// 命令中的长度,不包括SMSC信息长度,以数据字节计
sprintf(cmd, “AT+CMGS=%d\r”, nPduLength / 2 – nSmscLength); // 生成命令
// TRACE(“%s”, cmd);
// TRACE(“%s\n”, pdu);
WriteComm(cmd, strlen(cmd)); // 先输出命令串
nLength = ReadComm(ans, 128); // 读应答数据
// 根据能否找到”\r\n> “决定成功与否
if(nLength == 4 && strncmp(ans, “\r\n> “, 4) == 0)
{
return WriteComm(pdu, strlen(pdu));   // 得到肯定回答,继续输出PDU串
}
return 0;
}

Tagged , , ,

I2S & PCM

IIS通常会包括:时钟信号(BClk) ,数据线出 SDO,数据线入SDI (全双工的情况),还有是LRClk(专门用来区分左右声道,以此来同步。低电平代表左声道,高电平代表右声道)。每根数据线只传输2路信号(立体声),如果要传输6路信号,通常就是有3根数据出,以及3根数据入。
PCM有PCM-clock、PCM-sync、PCM-in、PCM-out四根线,其中PCM_SYNC用来同步帧,有long fram sync和short frame sync等模式,但并不专门区分左右声道。

I2S是音频数字化后数据排列的一种格式,说传输的就是PCM,支持单声道和立体声。
PCM(PCM-clock、PCM-sync、PCM-in、PCM-out)脉冲编码调制,模拟语音信号经过采样量化以及一定数据排列就是PCM了。理论上可以传输单声道,双声道立体声和多声道。是数字音频的raw data。

上图I2S时序

上图PCM时序

Tagged , , ,

Ubuntu下PL2303串口卡驱动

1、将设备u口插入pc

2、输入

#lsmod

先看看能否检测到这个设备,就看有没有pl2303字眼能了。如果有,则不必再装驱动。如果没有则需要驱动程式,一般硬件厂商都会提供,安装完毕后再输入这个命令瞧瞧,是否安装成功。

3、检测步骤,检测步骤和PCI转串口的8条以后内容如下,只是需要主意的是设置串口设备的时候可能不是ttyS2了,有可能是ttyUSB0之类的。

1、插入PCI卡到主机
2、启动 Linux,打开终端
3、输入命令:#setserial  /dev/ttyS0 -a   (COM-1)
显示内容:/dev/ttyS0, Line 0,  UART: 16550A, Port: 0x3f8, irq: 4
Baud_base: 115200, clos_delay: 50, divisor: 0
closing_wait: 3000, closing_wait2: infinite
Flags: spd_normal skip_test
4、输入命令:#setserial  /dev/ttyS2 -a   (COM-3)
显示内容:/dev/ttyS2, Line 2,  UART: unknown, Port: 0x3e8, irq: 4
Baud_base: 115200, clos_delay: 50, divisor: 0
closing_wait: 3000, closing_wait2: infinite
Flags: spd_normal skip_test
第3、4步操作的目的主要是对主机自带串口及PCI扩展串口的区别。区别在于4显示的内容中UART:未unknow。不过若您检测这一步的时候 UART为16550A而不是unknow,证明你的系统已经认识了扩展的串口,不需要进一步设置,直接跳入第8步测试就可以了。
5、需要输入命令查看一下您当前PCI检测的状态,以便对扩展串口进行设置
#more  /proc/pci
会显示出一堆的信息,不要因为看不懂而吓坏了。只要看到类似于这个PCI的信息,比如:PCI communication。。。或者Board with Nm9835CV part。。。   可能就是这个卡了,主要看看它的终端是多少,即irq多少及分配的地址是多少。例如:(不一定完全一样)
Board with Nm9835CV part  irq:11
I/O at 0xc000 [0xc001] serial port 1
I/O at 0xc400 [0xc401] serial port 2
I/O at 0xc800 [0xc801] not used
I/O at 0xd000 [0xd001] not used
I/O at 0xd400 [0xd401] not used
I/O at 0xd800 [0xd801] not used
6、知道PCI扩展卡的终端为11   串口1地址为0xc000  串口2地址为0xc400..
就可以设置扩展的串口了。输入命令:
setserial  /dev/ttyS2 port 0xc000 UART 16550A
irq 11 Baud_base 115200
另一个串口也类似的这么操作
7、设置完毕后,就可以看看设置的情况了,输入第2步的命令看看,UART是否就是16500A 而不是 unknow了,如果是16500A恭喜,可能设置好咯,如果不是那就再检查一下吧。
8、设置好了后是不是需要测试一下是否能够通讯呢?最好的办法是两台pc相连。如果pc为windows操作系统就用超级终端,是linux呢就用minicom吧
9、装有linux的机器,首先需要设置一下监听的串口参数,输入命令
#minicom -s
进入界面后有个框弹出来,如果你还认识点英文单词的话,就回知道选择哪个的。应该是第三个吧,串口设置。
将第一行更改为  /dev/ttyS2
波特率也更改您所需要的。
更改完后保存,保存的那个菜单应该是 save … df1
最后 exit
10、在另外一台机器发送数据,这台机器minicom界面就能够收到信息了,成功后觉得挺有意思。另外不要把两个COM顺序弄翻了,如果弄错了哪个是COM3  COM4测试可就不灵便咯。

Tagged , , , ,

使用Gmail的SyncML同步通讯录

“远程数据库”输入contacts,必须是小写字母。
“服务器识别码”为Google,字母G必须是大写。
“主机地址”输入https://m.google.com/syncml, “端口”为443,用户名为你的Gmail用户名,密码为Gmail密码。

Tagged ,

如何在iPhone上同步GoogleCalendar主日历以外的日历

By default only your primary calendar will be synced to your device. You can sync additional calendars by visiting the following page from any web browser:

https://www.google.com/calendar/iphoneselect

(Google Apps users can go to https://www.google.com/calendar/hosted/your_domain/iphoneselect, replacing ‘your_domain’ with your actual domain name.)

Select the calendars you’d like to sync, and click Save. The selected calendars will display on your device at the time of the next sync.

Tagged , ,

Ubuntu Server安装Apache+MySQL

1、 Apache

$ sudo apt-get install apache2

2、Mysql

$ sudo apt-get install mysql-server

3、PHP

$ sudo apt-get install php5

4、MySQL for Apache PHP (通信模板)

$ sudo apt-get install libapache2-mod-auth-mysql
$ sudo apt-get install php5-mysql

5、phpmyadmin

$ sudo apt-get install phpmyadmin

安裝後

1、打開 http://localhost 可以看到
It works!           表示apache正常運行

2、在終端輸入:

sudo netstat -tap | grep mysql

tcp 0 0 localhost.localdomain:mysql *:* LISTEN -      表示mysql正常運行

3、phpMyAdmin,會自動安裝在/usr/share/phpMyAdmin下(本人尝试多次未找到该目录,解决方案见后文)

在終端輸入

cp -r /usr/share/phpMyAdmin /var/www

將phpmyadmin文件復制到www文件夾下

打開 http://localhost/phpmyadmin                        打開mysql管理界面。

参考文献:http://www.pyoix.com/a/1228.html 《Ubuntu 10.10安裝Apache+MySQL+PHP環境》

=============技术宅的分割线=============

Should you get a 404 “Not Found” error when you point your browser to the location of phpMyAdmin (such as: http://localhost/phpmyadmin) this is likely caused by not checking the ‘Apache 2′ selection during installation. To redo the installation run the following:

sudo dpkg-reconfigure -plow phpmyadmin

Then select Apache 2 for the webserver you wish to configure.

If this does not work, then you can do the following to include the phpMyadmin-shipped Apache configuration into Apache:

sudo ln -s /etc/phpmyadmin/apache.conf /etc/apache2/conf.d/phpmyadmin.conf
sudo /etc/init.d/apache2 reload

参考文献:https://help.ubuntu.com/community/phpMyAdmin

Tagged , , , , ,

SleepCycle 88

SleepCycle应用只不过是在预设的起床时间前后测到翻身动作后提前闹铃而已,并无实际提高睡眠作用。他将退出我的iPhone桌面。

Tagged , ,

好吧,我承认我寂寞了一把。。。

Tagged , ,

电池电量检测

电压检测:

4.20V—100%,3.85V—75%,3.75V—50%,3.60V—25%,3.40V—5%。电池电压会随着RFPA的功率发射发生突变,通常会变小0.2V-0.3V。当前工程师们的普遍方法是利用软件算法进行均值滤波,对一段时间内的电池电压进行均值化,如果该时间段的平均电池电压确实下降了,则预估电量确实变少了,否则即认为电量并未变化。

电量检测:

通过库仑计实时监测电池消耗电量而计算剩余电量的方法则非常准确。Fairchild的FAN4010是一颗电流检测传感器,专门用于检测便携式设备电池的充电/耗电电流,能将通过精密检测电阻的电流信号转换为ADC可以检测到的电压信号,从而计算一段时间内消耗的真实电量。

Tagged , , , , ,

iPhone 4 芯片

Apple iPhone 4 – Front

  • Skyworks SKY77541 GSM/GPRS Front End Module
  • Triquint TQM666092 Power Amp
  • Skyworks SKY77452 W-CDMA FEM
  • Triquint TQM676091 Power Amp
  • Apple 338S0626 Infineon GSM/W-CDMA Transeiver
  • Skyworks SKY77459 Tx-Rx FEM for Quad-Band GSM/GPRS/EDGE
  • Apple AGD1 STMicro 3-axis digital gyroscope
  • Apple A4 Processor
  • Broadcom BCM4329FKUGB 802.11n with Bluetooth 2.1 + EDR and FM receiver
  • Broadcom BCM4750IUB8 single-chip GPS receiver

Apple iPhone 4 -Back

  • Apple 343S0499 – Texas Instruments Touchscreen controller
  • The Cirrus Logic 338S0589 audio codec
  • Samsung K9PFG08U5M 256G bit, x8 Flash MEMORY
  • 338S0867 Dialog (Die marks D1815A ‘Ashley’) Power Management Unit
  • 3383 Infineon X-GOLD 61x Baseband Processor
  • Intel 36My1EF – ELPIDA 128Mbits Mobile DDR SDRAM & 28F128FM Intel/Numonyx NOR

  • Omnivision OV5650  5百万像素 CMOS QSXGA image sensor
Tagged , , , , ,