莫斯精灵-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 15284|回复: 18

[项目] 莫斯精灵

[复制链接]
发表于 2019-3-10 22:43 | 显示全部楼层 |阅读模式

图1

图1
  在抗战和谍情影片中无线电台发出的嘀嘀嗒嗒声大家一定印象深刻吧?其实就是将信息转换成莫尔斯码再通过电台发射出去,发莫尔斯码的工具如上图所示。左边是人工将信息转换成莫尔斯码,速度慢,易出错,不易掌握;中间是电子键,内置了单片机,为半自动转换,速度有所提升,但易出错,不易掌握;右边是当前一些无线电爱好者使用的,为左边的改进版,特点与左边的相同。
  本人的项目是制作一款全新的莫尔斯码拍发设备,具备简单易用,准确高效等特点,本设备的出错将一统无线电江湖!看完本帖你会相信它具备这个实力!

图2

图2
  上图是本项目用到的材料,本人穷,都是凑合的,简单介绍一下哈。上边一排左边是arduino due,为什么选它?因为我只会使用arduino的板子,有大神告诉我只有due才能接收处理USB键盘的按键信息。Due角色为主MCU,主要是接收处理USB键盘的按键信息,以及除翻译莫尔斯电码外的其它事务。
  
游客,如果您要查看本帖隐藏内容请回复
  屏旁边的是供电部分,使用了2节三洋大红炮,动力强劲,一个充电放电保护一体板,一个可调2A升压板,因DUE要求供电不能低于6V(绝对不能使用5V供电!),升压板输出9V供电。
  再往右边的是arduinonano,担负从MCU的角色,通过控制模拟开关实现将字符翻译成莫尔斯码的功能。为什么采用主从双MCU设计呢?因为翻译莫尔斯电码时需要延时,而接收USB键盘信息使用的库中也用到了延时,如果使用一个MCU二者会相互影响,严重影响稳定性。
  再往右边是SD卡模块,很便宜,SD卡是报废手机里的。SD卡模块主要是实现保存、调用报文功能时用。
  最右边是模拟开关,使用的是国产的,ch440g,4路通道,导通电阻9欧姆左右,它是最终实现莫尔斯码的电气开关。
下面依次是蜂鸣器模块(发出电影里嘀嘀嗒嗒的声音),无锁按钮(作用是当本设备故障时,起手键的作用),电源开关,DC电源插座,洞洞板焊的母板(上边的USB母口用来连接USB键盘),一些连接线和螺钉,外壳(使用的华为手机的包装盒file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtmlclip1/01/clip_image001.gif)
  下面我们把它组装起来吧!

图3

图3
组装好设备上盖

图4

图4
  使用智能屏厂家提供的上位机软件设计各交互界面,设计好后将程序下载到智能屏。智能屏使用的串口,随便找一个下载工具就可以,只要电源正负不接反,若连通失败对倒一下收发。本项目不需要触摸功能,所以关闭了触摸功能,算是资源的浪费。

图5

图5
  组装设备下半部分,因身边没有电台,与电台相连的接口暂未安装。

图6

图6
设备运行后主界面图

图7

图7
设备运行后发报界面图

图8

图8
系统设置界面

图9

图9
关于界面
  本设备特点:本设备采用双MCU设计,无死机、变码、多码、连码等现象,运行稳定;采用通用标准USB键盘直接输入信息,拍发速度更快,更换方便;设置有应急按键;显示屏可一次性显示254个字符,操作者可在校对无误后再发送,提高了拍发准确率;界面美观交互性强;具备自动呼叫、报文调用、工作记录等功能;提供专用辅助工具。因保密原因部分功能简化了。
  目前第二版本正在顺利推进中,主要是代码优化、改进主从MCU的通信、供电、专用工具,增加上位机等,欢迎大家一起讨论。欢迎广大影视公司、无线电爱好者和特殊行业和我联系(本设备已申请专利)!
  本项目资料(主从MCU代码、智能屏文件、母板PCB):https://pan.baidu.com/s/179JGW6cy7WJXnMXp-g_5gg 提取码:fopm
或者直接扫描正面的二维码:

图10

图10


 楼主| 发表于 2019-3-10 23:15 | 显示全部楼层
[mw_shl_code=arduino,true]主MCU代码:
//due端,键盘输入、字符作用区分、屏幕显示、菜单控制
#include <SPI.h>
#include <SdFat.h>

#include <KeyboardController.h>
USBHost usb;
KeyboardController keyboard(usb);

#define STARTCHAR '{'//分段,开始
#define ENDCHAR '@'//完结
#define PAGECHAR '}'//句号
#define RADIOCHAR '^'//无线电信号
#define WARNCALL "XXX"//警报信号
#define DIAGONAL "/"//斜线
#define BRACKET "<"//括号
#define CORRECTS "?"//改错,数码
#define CORRECTL "|"//改错,字码、混合码
#define POINT "."//小数点
#define MAXSPEED 200//最大发报速度
#define MINSPEED 60//最小发报速度
#define FREELENGTHMAX 50//自由区内容最大长度
#define MSGMAXLENGTH 254//报文区最大字符数

//命令代码
#define AUTOCALLSTART 242//警报台自动呼叫开始
#define AUTOCALLSTOP 243//警报台自动呼叫结束
#define CHANGESPEED 240//调整速度命令,发给nano时用
#define RECORDSTART 246//开始记录发报内容
#define RECORDSTOP 247//结束记录发报内容
#define UPLOADLOG 248//通知Nano回传记录的发报内容
#define USEDATASTART 251//开始调用报底发报
#define USEDATAEND 252//结束调用报底发报
#define STOPFORREADY 250//通知Nano停止发报,并将除发报记录外的各参数复位
#define NEXTPAGE 126//Nano请求发下一页报文

SdFat sd;
const byte pinChipSelect =44;// 30;
const byte pinLedR = 38;//32;
const byte pinLedG = 36;//34;
const byte pinLedB = 34;//36;
//const byte pinDisplayPower=25;//此脚控制显示屏的供电
const byte numberLong=-15;

byte sys0=1;//电报的报文内容是随输随发1,还是一页一键发送0,还是调用发送2(调用报底发送时用)sendActId
//byte wordsTotal=0;//报文组数
//byte proFlag=0;//一般电报处理进程标识,0没有开始,1已发报头,2已发开始符号,3已发结束符号

int sendActId=1;//发报界面活动控件:0报文区,1自由勤务,2速度,3号数,4组数,5等级,6日期,7时间,8备注,9号数2,10组数2;调用报文界面:0种类,1页码,2组数;
byte actIdPre=0;//前一活动控件id
byte sys1=1;//报文分类:1电  报,2业务公电,3无线信号,4警报信号,5通信检查,0自由联络
byte scrId=1;//当前所处界面:0主界面main,1发报send,2自动呼叫autoCall,3调用报文useData,4工作日志workLog,5系统设置sysSet,6关于about,7输入对话框,8确认对话框
byte sys2=0;//标识数码电报是长码1还是短码0
byte endFlag=0xff;
byte saveOrUse=0;//标识调用自动呼叫界面时,是保存1还是调入0
byte autoCallIndex=0;
byte cSave=0;//标识当前是否记录工作日志

String msgFileName="";//文件名
//String msgFileName2="";//文件名

String msgSpeed="100";//范围60-250
String numberForNano="";
String numberForScrn="";
String msgGroup="";
String msgClass="";
String msgDate="";
String msgTime="";
String msgMarket="";
String msg="";//报文区内容,发往Nano的
String msgForDisplay="";//报文区内容,发往显示屏显示的
String freeForNano="";//自由区内容,发往nano的
String freeForScrn="";//自由区内容,发往显示屏的
//报底调用变量
byte dataKind=0;//报底种类:0数码(共有999页),1字码(共99页),2混合码(共99页)
bool dataSendStarted=false;//调用报底时使用,标识报底是否已发送 // note check kil
int dataPageNow=1;//当前界面显示发送的是第几页
int dataPage=1;//共有多少页
int dataGroup=100;//剩多少组没读取发送
int dataPositionFrom=0;//起始位置
//String dataMsg="";//调用报底内容
byte pgIndex=0;//系统调试项目下标

void closeOrOpenDisplay(){
  //digitalWrite(pinDisplayPower,!digitalRead(pinDisplayPower));
  return;
}
bool initDataNum(bool balance=false){//999页
  sd.remove("SYSDATANUM.txt");
  if (sd.exists("SYSDATANUM.txt")) {
    Serial.println(" delete fail !");
    return false;
  }
  randomSeed(analogRead(0));
  SdFile myFile("SYSDATANUM.txt", FILE_WRITE);
  if (myFile.isOpen()) {
    for(int i=1;i<1000;i++){
      for(int j=1;j<101;j++){
         myFile.print(random(10));
         myFile.print(random(10));
         myFile.print(random(10));
         myFile.print(random(10));
         myFile.print(" ");
      }}
    //myFile.save();
    myFile.close();
    Serial.println("init success !");
    return true;
  } else {
    Serial.println("write fail !");
    return false;
  }
}
bool initDataChr(bool balance=false){//99页
  sd.remove("SYSDATACHR.txt");
  if (sd.exists("SYSDATACHR.txt")) {
    Serial.println(" delete fail !");
    return false;
  }
  randomSeed(analogRead(0));
  SdFile myFile("SYSDATACHR.txt", FILE_WRITE);
  if (myFile.isOpen()) {
    for(int i=1;i<1000;i++){
      for(int j=1;j<101;j++){
         myFile.write(random(65,91));
         myFile.write(random(65,91));
         myFile.write(random(65,91));
         myFile.write(random(65,91));
         myFile.print(" ");
      }}
    myFile.close();
    Serial.println("init success !");
    return true;
  } else {
    Serial.println("write fail !");
    return false;
  }
}
bool initDataBoth(){//99页
  sd.remove("SYSDATABOTH.txt");
  if (sd.exists("SYSDATABOTH.txt")) {
    Serial.println(" delete fail !");
    return false;
  }
  randomSeed(analogRead(0));
  SdFile myFile("SYSDATABOTH.txt", FILE_WRITE);
  if (myFile.isOpen()) {
    for(int i=1;i<100;i++){
      for(int j=1;j<101;j++){
         if(random(2)){
            myFile.write(random(48,58));
         }else{
            myFile.write(random(65,91));}
         if(random(2)){
            myFile.write(random(48,58));
         }else{
            myFile.write(random(65,91));}
         if(random(2)){
            myFile.write(random(48,58));
         }else{
            myFile.write(random(65,91));}
         if(random(2)){
            myFile.write(random(48,58));
         }else{
            myFile.write(random(65,91));}
         myFile.print(" ");
      }}
    myFile.close();
    Serial.println("init success !");
    return true;
  } else {
    Serial.println("write fail !");
    return false;
  }
}
//读取1页系统报底,2个使用场合:调用发送时调用报底,供sendDataNextPage调用
bool readData(){
  if(dataGroup<=0)return false;

  String tem="";  
  char* fileName;
  unsigned int dataPositionEnd=0;

  if(dataGroup>=100){
    dataPositionEnd=dataPositionFrom+500;
    dataGroup=dataGroup-100;
  }else{
    dataPositionEnd=dataPositionFrom+dataGroup*5;
    dataGroup=0;
  }
  if(dataKind==0){
    fileName="SYSDATANUM.txt";
    }else if(dataKind==1){
      fileName="SYSDATACHR.txt";
    }else{
      fileName="SYSDATABOTH.txt";
      }

  SdFile myFile4(fileName, FILE_READ);

  if (myFile4.isOpen()) {
    myFile4.seekSet(dataPositionFrom);
    while (dataPositionFrom<=dataPositionEnd && myFile4.available()) {
      tem=tem + char(myFile4.read());
    }
    myFile4.close();
    dataPositionFrom=dataPositionEnd;
    Serial.println("read success");
    if(dataGroup){
      msgForDisplay=String(STARTCHAR) + tem + String(PAGECHAR);
    }else{
      msgForDisplay=String(STARTCHAR) + tem + String(ENDCHAR);
    }
    return true;
  } else {
    Serial.println("open error");
    return false;
  }
}
String replaceNumberToLong(String msg){
  msg.replace('0','!');
  msg.replace('1','"');
  msg.replace('2','#');
  msg.replace('3','$');
  msg.replace('4','%');
  msg.replace('5','&');
  msg.replace('6','\'');
  msg.replace('7','(');
  msg.replace('8',')');
  msg.replace('9','*');
  return msg;
}
//读取并使用1页报底,使用在2个地方:Nano发来的请求命令,调用发送时在报文区按回车开始发送(fistPage=true)
bool sendDataNextPage(bool fistPage=false){  
  if(!fistPage){
    if(!readData())return false;
    cmdToDisplay("t10.txt=\"" + msgForDisplay + "\"");//显示报文

  }else{
    byteToNano(USEDATASTART);
    dataSendStarted=true;//标识开始发送
  }
  dataPageNow=dataPageNow+1;
  cmdToDisplay("nPage.txt=\"" + String(dataPageNow) + "/" + String(dataPage) + "\"");//更新界面页码
  msg=msgForDisplay;
  if(sys2==1 || dataKind==2){//数字发长码
    msg=replaceNumberToLong(msg);
  }
  msgToNano(msg);
  if(dataPageNow>=dataPage){
      byteToNano(USEDATAEND);//已发送完,通知Nano,且发送模式自动变为一键发送
      sys0=0;
      cmdToDisplay("sys0=0");
      cmdToDisplay("click t11,0");
      return false;
  }
}
//读worklog
String readLog(){
  String msg="";  
  SdFile myFile("WORKLOG.txt", FILE_READ);
  if (myFile.isOpen()) {
    while (myFile.available()) {
      msg=msg + String(char(myFile.read()));
    }
    myFile.close();
    Serial.println(msg);
    return msg;
    } else {
    Serial.println("read error");
    myFile.close();
    return "";
  }
}
//电报记录
bool saveLog(String fileContent){
  //SdFile myFile;
  File myFile;
  myFile = sd.open("WORKLOG.txt", FILE_WRITE);//Nano的内容
  if (myFile.isOpen()) {
    myFile.println(fileContent);
    myFile.close();
    Serial.println("SAVELOG ok !");
    return true;
  }else{
    Serial.println("SAVELOG FAIL !");
    myFile.close();
    return false;
  }
}

//保存自动呼叫
bool saveCall(){
   freeForScrn.replace("  "," ");
   freeForScrn.replace("  "," ");
   freeForScrn.replace("  "," ");
   String call=String(autoCallIndex) + "." +freeForScrn;
   SdFile myFile("AUTOCALL.txt", FILE_WRITE);
   if (myFile.isOpen()) {
      Serial.println("writing:" + call);
      myFile.seekSet(15*autoCallIndex);
      myFile.print(call);
      myFile.close();
      autoCallIndex=0;
      Serial.println("save ok ...");
      return true;
   }
   myFile.close();
   autoCallIndex=0;
   return false;
}
bool initAutocall(){//初始化自动呼叫文件
  SdFile myFile("AUTOCALL.txt", FILE_WRITE);
  if (!myFile.isOpen())return false;
  for(byte i=0;i<10;i++){
    myFile.seekSet(15*i);
    myFile.print(String(i)+".             ");
  }
  myFile.close();
  return true;
}
//读取全部呼叫
String readAll(){
  String tem="";
  for(int i=0;i<10;i++){
     tem=tem + readCall(i) + char(13) + char(10);
  }
  Serial.println(tem);
  return tem;
}
//读取单条呼叫,主要被readAll调用
String readCall(byte index){
  String msg="";
  byte i=0;

  SdFile myFile("AUTOCALL.txt",FILE_READ);
  if (myFile.isOpen()) {
    myFile.seekSet(15*index);
    while (i<15 && myFile.available()) {
      i++;
      msg=msg + String(char(myFile.read()));
    }
    myFile.close();
    Serial.println(msg);
    return msg;
    } else {
  Serial.println("read error");
  myFile.close();
  return "";
  }
}
bool saveAs(char * fileName){
  SdFile myFile("WORKLOG.txt", FILE_READ);
  if (!myFile.isOpen())return false;
  if (!myFile.rename(sd.vwd(),fileName)) return false;
  return true;
}
//发往显示屏的字符
bool charToDisplay(String cmd){
  cmdToDisplay("send.valStrS.txt=\"" + cmd + "\"");
  cmdToDisplay("click btnDisplay,1");
}
//发往显示屏的命令
bool cmdToDisplay(String cmd){
  Serial.println(cmd);
  //return true;
  Serial1.print(cmd);
  Serial1.write(endFlag);
  Serial1.write(endFlag);
  Serial1.write(endFlag);
  return true;
  }
//发往Nano的消息
bool msgToNano(String msg){
  Serial3.print(msg);
  Serial.println(msg);
  return true;
}
bool byteToNano(byte msg){
  Serial3.write(msg);
  Serial.println(msg);
  return true;
}
//速度值验证
void checkSpeed(){
  //if(sendActId || scrId!=1)return;
  int temVal=msgSpeed.toInt();
  if(temVal>=60 && temVal<=250)return;
  if(temVal<60)temVal=60;
  if(temVal>250)temVal=250;  
  msgSpeed=String(temVal);
  byteToNano(CHANGESPEED);
  byteToNano(byte(msgSpeed.toInt()));
  cmdToDisplay("t0.txt=\"" + msgSpeed + "\"");
}
//号数中0的处理,若号数为数字与字母混合,数字0发长码,被sendEnter调用
String SLTranslate(String numberForNano){
  if(numberForNano.indexOf("/")>-1 && numberForNano.indexOf("0")>-1){
    numberForNano.replace('0','!');
    }
  return numberForNano;
}

//所有退格的处理
void proBackSpace(){
  switch(sendActId){
    case 0:
      msgSpeed=msgSpeed.substring(0,msgSpeed.length()-1);
      cmdToDisplay("t0.txt=t0.txt-1");
      break;
    case 1:
      freeForNano=freeForNano.substring(0,freeForNano.length()-1);
      freeForScrn=freeForScrn.substring(0,freeForScrn.length()-1);
      cmdToDisplay("t1.txt=t1.txt-1");
      break;
    case 2:
      numberForNano=numberForNano.substring(0,numberForNano.length()-1);
      numberForScrn=numberForScrn.substring(0,numberForScrn.length()-1);
      cmdToDisplay("t2.txt=t2.txt-1");
      break;
    case 3:
      msgGroup=msgGroup.substring(0,msgGroup.length()-1);
      cmdToDisplay("t3.txt=t3.txt-1");
      break;
    case 4:
      msgClass=msgClass.substring(0,msgClass.length()-1);
      cmdToDisplay("t4.txt=t4.txt-1");
      break;
    case 5:
      msgDate=msgDate.substring(0,msgDate.length()-1);
      cmdToDisplay("t5.txt=t5.txt-1");
      break;
    case 6:
      msgTime=msgTime.substring(0,msgTime.length()-1);
      cmdToDisplay("t6.txt=t6.txt-1");
      break;
    case 7:
      numberForNano=numberForNano.substring(0,numberForNano.length()-1);
      numberForScrn=numberForScrn.substring(0,numberForScrn.length()-1);
      cmdToDisplay("t7.txt=t7.txt-1");
      break;
    case 8:
      msgGroup=msgGroup.substring(0,msgGroup.length()-1);
      cmdToDisplay("t8.txt=t8.txt-1");
      break;
    case 9:
      msgMarket=msgMarket.substring(0,msgMarket.length()-1);
      cmdToDisplay("t9.txt=t9.txt-1");
      break;
    case 10://发报界面-报文区
      msg=msg.substring(0,msg.length()-1);
      msgForDisplay=msgForDisplay.substring(0,msgForDisplay.length()-1);
      cmdToDisplay("t10.txt=t10.txt-1");
      break;
    case 11://调用报文界面-报文种类
      break;
    case 12://调用报文界面-起始页码
      dataPage=dataPage/10;
      //if(dataPage)dataPage=1;
      cmdToDisplay("n1.val=" + String(dataPage));
      break;
    case 13://调用报文界面-调用组数
      dataGroup=dataGroup/10;
      //if(dataGroup)dataGroup=1;
      cmdToDisplay("n2.val=" + String(dataGroup));
      break;
    case 71://输入对话框
      msgFileName=msgFileName.substring(0,msgFileName.length()-1);
      cmdToDisplay("t0.txt=t0.txt-1");
      break;
    default:
      break;  
    }
}
//所有清空的处理
void proDel(){
  switch(sendActId){
    case 0:
      msgSpeed="";
      cmdToDisplay("t0.txt=\"\"");
      break;
    case 1:
      freeForNano="";
      freeForScrn="";
      cmdToDisplay("t1.txt=\"\"");
      break;
    case 2:
      numberForNano="";
      numberForScrn="";
      cmdToDisplay("t2.txt=\"\"");
      break;
    case 3:
      msgGroup="";
      cmdToDisplay("t3.txt=\"\"");
      break;
    case 4:
      msgClass="";
      cmdToDisplay("t4.txt=\"\"");
      break;
    case 5:
      msgDate="";
      cmdToDisplay("t5.txt=\"\"");
      break;
    case 6:
      msgTime="";
      cmdToDisplay("t6.txt=\"\"");
      break;
    case 7:
      numberForNano="";
      numberForScrn="";
      cmdToDisplay("t7.txt=\"\"");
      break;
    case 8:
      msgGroup="";
      cmdToDisplay("t8.txt=\"\"");
      break;
    case 9:
      msgMarket="";
      cmdToDisplay("t9.txt=\"\"");
      break;
    case 10:
      if(sys0==2)return;
      msg="";
      msgForDisplay="";
      cmdToDisplay("t10.txt=\"\"");
      break;
    case 11://调用报文界面-报文种类
      break;
    case 12://调用报文界面-起始页码
      dataPage=0;
      cmdToDisplay("n1.val=0");
      break;
    case 13://调用报文界面-调用组数
      dataGroup=0;
      cmdToDisplay("n2.val=0");
      break;
    case 71://输入对话框
      msgFileName="";
      cmdToDisplay("t0.txt=\"\"");
      break;
  }
}
//隐藏字符输入验证及处理;按下ctrl键即响应
void checkHide(byte chOemKey,int chMod){//扫描码,控制键状态(ctrl alt shift)
  Serial.println(chOemKey);
  //Serial.println(chMod);
  //-------del(76)-------清空
  if(chOemKey==76){
    proDel();
    return;
  }
  //-------backSpace(42)-------退格
  if(chOemKey==42){
    proBackSpace();
    return;
  }

  if(!chMod)return;

  //--------F12(69)--------焦点定位移动至报文区
  if(chOemKey==69){
    if(sendActId==0)checkSpeed();//纠正速度值至合理范围
    if(sendActId<10){//当为10时已在报文区,故不响应
      sendActId=10;
      cmdToDisplay("click t1,0");
      cmdToDisplay("send.sendActId.val=10");
    }
    return;
  }
  //--------F11(68)-------焦点定位移动至自由区
  if(chOemKey==68){
    if(sendActId==0)checkSpeed();
    if(sendActId<11 && sendActId!=1){
      sendActId=1;
      cmdToDisplay("click t1,0");
      cmdToDisplay("send.sendActId.val=1");
    }
    return;
  }
  //--------左箭头(80)-------焦点前移
  if(chOemKey==80){
    if(sendActId<21)cmdToDisplay("click t1,0");//先去掉原光标符号
    switch(sendActId){
      case 0:
        checkSpeed();//纠正速度值
        sendActId=10;
        break;
      case 1:
        sendActId=0;
        break;
      case 2:
        sendActId=1;
        break;
      case 3:
        sendActId=2;
        break;
      case 4:
        sendActId=3;
        break;
      case 5:
        sendActId=4;
        break;
      case 6:
        sendActId=5;
        break;
      case 7:
        sendActId=1;
        break;
      case 8:
        sendActId=7;
        break;
      case 9:
        if(sys1==1)
          sendActId=6;
        else
          sendActId=8;
        break;
      case 10:
        sendActId=9;
        break;
      case 11://调用报文界面-报文种类(理论上不会为11)
        sendActId=13;
        cmdToDisplay("n1.picc=2");
        cmdToDisplay("n2.picc=4");
        break;
      case 12://调用报文界面-起始页码
        sendActId=13;
        cmdToDisplay("n1.picc=2");
        cmdToDisplay("n2.picc=4");
        break;
      case 13://调用报文界面-调用组数
        sendActId=12;
        cmdToDisplay("n1.picc=4");
        cmdToDisplay("n2.picc=2");
        break;
      case 51://计算器第1个数
        sendActId=52;
        cmdToDisplay("n0.picc=2");
        cmdToDisplay("n1.picc=4");
        break;
      case 52://计算器第1个数
        sendActId=51;
        cmdToDisplay("n0.picc=4");
        cmdToDisplay("n1.picc=2");
        break;
      case 71://输入对话框
        break;
      default:
        if(sendActId>=20 && sendActId<=25){//---main界面---
          cmdToDisplay("click b" + String(sendActId-20) + ",0");
          if(sendActId==20){
            sendActId=25;
          }else{
            sendActId=sendActId-1;
          }
          cmdToDisplay("click b" + String(sendActId-20) + ",1");
        }
        return;
    }
    cmdToDisplay("send.sendActId.val=" + String(sendActId));
    return;
  }
  //-------右箭头(79)-------焦点后移
  if(chOemKey==79){
    if(sendActId<21)cmdToDisplay("click t1,0");//先去掉原光标符号
    switch(sendActId){
      case 0:
        checkSpeed();//纠正速度值
        sendActId=1;
        break;
      case 1:
        if(sys1==1)
          sendActId=2;
        else
          sendActId=7;
        break;
      case 2:
        sendActId=3;
        break;
      case 3:
        sendActId=4;
        break;
      case 4:
        sendActId=5;
        break;
      case 5:
        sendActId=6;
        break;
      case 6:
        sendActId=9;
        break;
      case 7:
        sendActId=8;
        break;
      case 8:
        sendActId=9;
        break;
      case 9:
        sendActId=10;
        break;
      case 10:
        sendActId=0;
        break;
      case 11://调用报文界面-报文种类(理论上不会为11的)
        sendActId=12;
        cmdToDisplay("n1.picc=4");
        cmdToDisplay("n2.picc=2");
        break;
      case 12://调用报文界面-起始页码
        sendActId=13;
        cmdToDisplay("n1.picc=2");
        cmdToDisplay("n2.picc=4");
        break;
      case 13://调用报文界面-调用组数
        sendActId=12;
        cmdToDisplay("n1.picc=4");
        cmdToDisplay("n2.picc=2");
        break;
      case 51://计算器第1个数
        sendActId=52;
        cmdToDisplay("n0.picc=2");
        cmdToDisplay("n1.picc=4");
        break;
      case 52://计算器第1个数
        sendActId=51;
        cmdToDisplay("n0.picc=2");
        cmdToDisplay("n1.picc=4");
      case 71://输入对话框
        break;
      default:
        if(sendActId>=20 && sendActId<=25){//---main界面---
          cmdToDisplay("click b" + String(sendActId-20) + ",0");
          if(sendActId==25){
            sendActId=20;
          }else{
            sendActId=sendActId+1;
          }
          cmdToDisplay("click b" + String(sendActId-20) + ",1");
        }
    }
    cmdToDisplay("send.sendActId.val=" + String(sendActId));
    return;
  }

  //-------F9(66)-------调整工作业务类型,仅发报界面
  if(chOemKey==66){
    if(sendActId<=10){
      //wordsTotal=0;//报文组数
      //proFlag=0;
      if(sys1==5){
        sys1=1;
        if(sendActId==7)sendActId=2;
        if(sendActId==8)sendActId=3;
      }else{
        sys1+=1;
        if(sendActId==2)sendActId=7;
        if(sendActId>2 && sendActId<7)sendActId=8;
      }
      msgToNano(String(char(AUTOCALLSTOP)));// 通知NANO暂停警报台自动呼叫(249命令不存在,但nano收到任何信息都会先暂停自动呼叫)
      cmdToDisplay("sys1=" + String(sys1));
      cmdToDisplay("click t21,0");//改显示屏上报文类型
    }
    return;
  }
  //-------上箭头(82)---------调高数值,改变枚举变量类型内容
  if(chOemKey==82){
     if(chMod==3){//ctrl+shift,调高屏幕亮度,弹起事件
        cmdToDisplay("click tDisplayCtr,0");
        return;
     }
     if(sendActId==0){//发报界面速度控件t0接受,调高发报速度
       if(msgSpeed.toInt()+5<=MAXSPEED){
          msgSpeed=String(msgSpeed.toInt()+5);
          cmdToDisplay("t0.txt=\"" + msgSpeed + "\"");
          byteToNano(CHANGESPEED);
          byteToNano(byte(msgSpeed.toInt()));}
       return;
     }
     if(sendActId==31){//自动呼叫界面,循环递增自动呼叫下标0-9
        if(autoCallIndex==9){
          autoCallIndex=0;
        }else{
          autoCallIndex++;
        }
        cmdToDisplay("t1.txt=\"" + String(autoCallIndex) + "\"");
        return;
     }
     if(sendActId>=11 && sendActId<=19){//调用报底界面:循环改变报底种类:数码、字码、混合码
        if(dataKind==2)
           dataKind=0;
        else
           dataKind++;
        cmdToDisplay("va0.val=" + String(dataKind));
        cmdToDisplay("click t3,0");
     }
     if(sendActId==101){//系统调试
        if(pgIndex==9){
          pgIndex=0;
        }else{
          pgIndex++;
        }
        cmdToDisplay("t1.txt=\"" + String(pgIndex) + "\"");
        return;
     }
     if(sendActId==51 || sendActId==52){//计算器,循环改变运算符+-*/%
        cmdToDisplay("click t3,1");
        return;
     }
     return;
  }
  //-------下箭头(80)--------调低数值
  if(chOemKey==81){
     if(chMod==3){//ctrl+shift,调暗屏幕,按下事件
        cmdToDisplay("click tDisplayCtr,1");
        return;
     }
     if(sendActId==0){//发报界面速度控件t0,调低发报速度
       if(msgSpeed.toInt()-5>=MINSPEED){
          msgSpeed=String(msgSpeed.toInt()-5);
          cmdToDisplay("t0.txt=\"" + msgSpeed + "\"");
          byteToNano(CHANGESPEED);
          byteToNano(byte(msgSpeed.toInt()));}
       return;
     }
     if(sendActId==31){//自动呼叫界面,循环递减自动呼叫下标0-9
        if(autoCallIndex==0){
          autoCallIndex=9;
        }else{
          autoCallIndex--;
        }
        cmdToDisplay("t1.txt=\"" + String(autoCallIndex) + "\"");
        return;
     }
     if(sendActId>=11 && sendActId<=19){//调用报底界面:循环改变报底种类:数码、字码、混合码
        if(dataKind==0)
           dataKind=2;
        else+
           dataKind--;
        cmdToDisplay("va0.val=" + String(dataKind));
        cmdToDisplay("click t3,0");
        return;
     }
     if(sendActId==51 || sendActId==52){//计算器,循环改变运算符+-*/%
        cmdToDisplay("click t3,0");
        return;
     }
     if(sendActId==73){//自动呼叫界面,循环递减自动呼叫下标0-9
        if(pgIndex==0){
          pgIndex=9;
        }else{
          pgIndex--;
        }
        cmdToDisplay("t1.txt=\"" + String(pgIndex) + "\"");
        return;
     }
     return;
  }
  //--------F10(67)---------调整发送模式,仅发报界面
  if(chOemKey==67){
    if(sendActId<=10){
      if(sys0>=1){
        sys0=0;
        }else{
          sys0++;
        }
      cmdToDisplay("sys0=" + String(sys0));
      cmdToDisplay("click t11,0");
    }
    return;
  }
  //--------F8(65)---------调整长短码,仅发报界面
  if(chOemKey==65){
     if(sendActId<=10){
        if(sys2==1){
          sys2=0;
          cmdToDisplay("sys2=0");
        }else{
          sys2=1;
          cmdToDisplay("sys2=1");
        }
        cmdToDisplay("click t12,0");
     }
     return;
  }
  //--------home()74-------返回主界面
  if(chOemKey==74){
    sendActId=20;
    cmdToDisplay("page main");
    return;
    }
  //--------F1(58)-------保存,工作日志的保存
  if(chOemKey==58){
    if(sendActId<=10 && cSave){//仅发报界面且处于保存状态下时响应
       actIdPre=sendActId;
       sendActId=71;//输入对话框界面
       cmdToDisplay("page inputBox");
       cmdToDisplay("t0.txt=\"" + msgFileName + "\"");//此处没有赋空串值,在不重启情况下默认为上次输入的名字
       byteToNano(UPLOADLOG);//让nano把剩余的、未满页的内容发过来
    }
    return;
  }
  //--------F2(59)-------保存 //警报呼叫:自呼与被用的保存
  if(chOemKey==59){
      if(sendActId<=10 && sys1==4){//仅发报界面,且为警报信号时响应
         actIdPre=sendActId;
         sendActId=31;
         saveOrUse=1;
         autoCallIndex=0;
         cmdToDisplay("page autoCall");
         cmdToDisplay("va0.val=1");//标识保存
         cmdToDisplay("click t1,0");
         cmdToDisplay("t2.txt=\"" + readAll() + "\"");//将当前的自呼与自用全部读出展示
         }     
      return;
   }
  //--------F3(60)-------报底调用,仅用于电报(调入系统报底、工作记录)、警报呼叫(调入自动呼叫的自呼和被用)
  if(chOemKey==60){
    if(sendActId<=10){
      if(sys1==1){//发界面,电报,报底的调入
         dataPage=1;
         dataGroup=100;
         dataKind=0;
         actIdPre=sendActId;
         sendActId=12;//激活控件id
         cmdToDisplay("page useData");
         cmdToDisplay("va0.val=0");
         cmdToDisplay("n1.picc=4");
         cmdToDisplay("n2.picc=2");
         return;
      }
      if(sys1==4){//发界面,警报呼叫,调入自呼与被用
         actIdPre=sendActId;
         sendActId=31;//激活控件id
         saveOrUse=0;
         autoCallIndex=0;
         cmdToDisplay("page autoCall");
         cmdToDisplay("va0.val=0");//标识调用
         cmdToDisplay("click t1,0");
         cmdToDisplay("t2.txt=\"" + readAll() + "\"");
         return;
      }
      return;
    }
    return;
    }
  //--------F4(61)-------日志保存开始/结束切换(若要保存日志,必须先保存,再结束,因为一但结束就会清空日志缓存变量)
  if(chOemKey==61){
     if(sendActId<=10){
       if(cSave){
         cSave=0;
         byteToNano(RECORDSTART);
       }else{
         cSave=1;
         byteToNano(RECORDSTOP);
       }
       cmdToDisplay("cSave.val=" + String(cSave));//更新显示显示屏上的选择框
       if(cSave)sd.remove("WORKLOG.txt");//删除原日志文件,可能会有上次的记录(如上次开始记录了,但没有保存)
     }
     return;
  }
  //--------ESC(41)-------取消
  if(chOemKey==41){
     if((sendActId>=11 && sendActId<=19) || sendActId==31 || sendActId==71){
        sendActId=actIdPre;
        if(sendActId>10)sendActId=1;//防止意外发生
        cmdToDisplay("page send");
        return;
     }
     if(sendActId<=10 || sendActId==41 || sendActId==91 || sendActId==51 || sendActId==52){
        sendActId=20;
        cmdToDisplay("page main");
     }
     return;
    }
  //--------PgUp(73)-------上一页
  if(chOemKey==173){//暂时取消
     return;
     if(sendActId<=10 && sys0==2){//发报界面,调用报底,显示上一页,循环式
        if(dataPageNow==1){//当前为第一页则显示最后一页
          dataPageNow=dataPage;
        }else{
          dataPageNow=dataPageNow-1;
        }
        msgForDisplay=msg.substring((dataPageNow-1)*500,499);//报文全部读出,需视情更改
        cmdToDisplay("t10.txt=\"" + msgForDisplay + "\"");//显示第一页报文
        cmdToDisplay("nPage.val="+dataPageNow);//更新界面页码
        }
      return;
    }
  //--------PgDn(81)-------下一页
  if(chOemKey==81){//暂时取消
     return;
     if(sendActId<=10 && sys0==2){//发报界面,调用报底,显示下一页
        if(dataPageNow==dataPage){
          dataPageNow=1;
        }else{
          dataPageNow=dataPageNow+1;
        }
        msgForDisplay=msg.substring((dataPageNow-1)*500,499);
        cmdToDisplay("t10.txt=\"" + msgForDisplay + "\"");//显示第一页报文
        cmdToDisplay("nPage.val="+dataPageNow);//更新界面页码
        }
      return;
    }
  //--------PrtSc(55)-------打开、关闭显示屏(控制显示屏的供电)
  if(chOemKey==55){
     closeOrOpenDisplay();
    }
  //--------insert(73)-------编程调试
  if(chOemKey==73){
     sendActId=101;
     pgIndex=0;
     cmdToDisplay("page sysPg");
    }
}
//所有界面回车的处理sendActId
void sendEnter(byte sys,int chMod){
  String tem;
  switch(sys){
    case 0://在速度区按回车,速度值纠正,并焦点移动至自由区kill
      checkHide(byte(79),1);
      break;
    case 1://在自由区按回车
      if(sys1==4){//若警报业务,则自动呼叫开始
        freeForNano.trim();
        if(!freeForNano.length())return;//空,不响应
        Serial.println("warn auto call start !");
        msgToNano(String(char(AUTOCALLSTART)) + freeForNano + String(char(AUTOCALLSTART)));
      }else{
        msgToNano(freeForNano);
        }
      break;
    case 2://在2--8按回车等价于后移焦点
      return checkHide(79,1);
      break;
    case 3:
      return checkHide(79,1);
      break;
    case 4:
      return checkHide(79,1);
      break;
    case 5:
      return checkHide(79,1);
      break;
    case 6:
      return checkHide(79,1);
      break;
    case 7:
      return checkHide(79,1);
      break;
    case 8:
      return checkHide(79,1);
      break;
    case 9://附注区
      //if(proFlag>0)return checkHide(79,1);//报头已发,跳至报文区
      if(sys1==1){//为电报业务或业务公电,在附注区按回车发送电报头
        numberForScrn.trim();
        msgClass.trim();
        msgDate.trim();
        msgTime.trim();
        msgMarket.trim();

        if(numberForScrn.length()<1)return;//交付时更改条件
        if(msgGroup.toInt()<1)return;
        if(msgClass.length()<1)return;
        if(msgDate.length()<1)return;
        if(msgTime.length()<1)return;
        tem="NR " + SLTranslate(numberForScrn) + " CK " + msgGroup + " " + msgClass + " " + msgDate + " " + msgTime;
        if(msgMarket.length()>0)tem=tem +" RMKS " + msgMarket;
      }else{
        if(sys1==4)return;
        numberForScrn.trim();
        msgTime.trim();
        msgMarket.trim();
        if(numberForScrn.length()<1)return;//交付时更改条件
        if(msgTime.length()<1)return;
        tem="NR " + SLTranslate(numberForScrn) + " " + msgTime;
        if(msgMarket.length()>0)tem=tem +" RMKS " + msgMarket;
      }
      msgToNano(tem);
      checkHide(79,1);//调用右箭头
      break;
    case 10://报文区
      msg.trim();//去掉空格
      msgForDisplay.trim();
      if(!chMod){
        msgForDisplay=msgForDisplay + char(13)+char(10);
        charToDisplay(String(char(13)));
        charToDisplay(String(char(10)));
        msg=msg+" ";
        return;
      }
      //以下为用户发出整页发送命令
      if(msg.length()<1)return;
      if(sys1==1){//电报,一次性发送或调用发送
         if(sys0==1)return;
         if(sys0==0){
           if(!msg.startsWith("{") && !dataSendStarted)msg=msg+"{";
           msgToNano(msg);
           msg="";//一次性发送后清屏
           msgForDisplay="";
           cmdToDisplay("t10.txt=\"\"");
           dataSendStarted=true;//标识已经开始发送了
         }
         if(sys0==2){
           sendDataNextPage(true);
         }
       return;;
      }
      if(sys1==2){//业务公电,一次性发送
         msgToNano(msg);
         return;
        }
      if(sys1==3){//无线电信号,一次性发送
         msgToNano(String(RADIOCHAR) + " NR " + numberForNano + " " + msgTime + " " + String(STARTCHAR) + " " + msg + " " + String(ENDCHAR));
         return;
        }
      if(sys1==4){//发警报信号,nano处于自动警报呼叫时,收到数据即停止自动呼叫!
         byteToNano(AUTOCALLSTOP);
         msgToNano("XXX "+ String(STARTCHAR)+ " " + msg + " " + String(ENDCHAR) + "     XXX "+ String(STARTCHAR) + " " + msg + " " + String(ENDCHAR));
         return;;
        }
      if(sys1==5){//通信检查,一次性发送
         if(msg.indexOf("{")==-1)msgToNano("V V V " + String(STARTCHAR) + " " + msg + " " + msg + " " + msg + " " + String(ENDCHAR));
        }
      break;  
    case 11://报底调用,回车后仅当前显示改变,不发送
      if(dataPage<1 || dataGroup<1) return;//非法,不处理
      dataPositionFrom=(dataPage-1)*500;

      if(!readData())return;

      dataPage=dataGroup/100;
      if(dataGroup%100)dataPage=dataPage+1;
      dataPageNow=0;
      dataSendStarted=false;
      //界面显示
      sendActId=actIdPre;
      sys0=2;
      cmdToDisplay("page send");//返回发报界面
      cmdToDisplay("sys0=2");//此处两行更改发送模式为“调用发送”
      cmdToDisplay("t10.txt=\"" + msgForDisplay + "\"");//显示第一页报文
      cmdToDisplay("nPage.val=0/"+String(dataPage));//更新界面页码
      break;
    case 12:  
      sendEnter(11,chMod);
      break;
    case 13:
      sendEnter(11,chMod);
      break;
    case 20:
      sendActId=1;
      actIdPre=20;
      cmdToDisplay("page send");
      break;
    case 21:
      sendActId=31;
      actIdPre=20;
      saveOrUse=2;
      autoCallIndex=0;
      cmdToDisplay("page autoCall");
      cmdToDisplay("va0.val=2");//标识浏览
      cmdToDisplay("click t1,0");
      cmdToDisplay("t2.txt=\"" + readAll() + "\"");
      break;
    case 22:
      sendActId=12;
      cmdToDisplay("page useData");
      break;
    case 23://辅助工具`
      sendActId=51;
      cmdToDisplay("page tool");
      break;
    case 24://系统设置
      sendActId=41;
      cmdToDisplay("page sysSet");
      break;
    case 41:
      sendActId=20;
      cmdToDisplay("page main");
      break;
    case 25://关于
      sendActId=91;
      cmdToDisplay("page about");
      break;
    case 91:
      sendActId=20;
      cmdToDisplay("page main");
      break;
    case 31://自动呼叫的回车
      sendActId=actIdPre;
      if(actIdPre==20){
         cmdToDisplay("page main");
         return;
      }else{
         cmdToDisplay("page send");
      }
      if(saveOrUse==1){//保存
         saveCall();
       }else{//调用
         freeForScrn=readCall(autoCallIndex);
         freeForScrn.trim();
         freeForScrn=freeForScrn.substring(2);
         cmdToDisplay("t1.txt=\"" + freeForScrn + "\"");
         freeForNano=replaceNumberToLong(freeForScrn);
       }
      break;
    case 101:
      sendActId=1;
      cmdToDisplay("page send");
      switch(pgIndex){
        case 0://初始化自动呼叫文件
          if(initAutocall()){
            Serial.println("initAutocall init ok !");
          }else{
            Serial.println("initAutocall init fail !");
          }
          break;
        case 1://初始化数码报底
          if(initDataNum()){
            Serial.println("initDataNum init ok !");
          }else{
            Serial.println("initDataNum init fail !");
          }
          break;
        case 2://初始化字码报底
          if(initDataChr()){
            Serial.println("initDataChr init ok !");
          }else{
            Serial.println("initDataChr init fail !");
          }
          break;
        case 3://初始化混合报底
          if(initDataBoth()){
            Serial.println("initDataBoth init ok !");
          }else{
            Serial.println("initDataBoth init fail !");
          }
          break;
      }

      break;
    case 51:
      cmdToDisplay("click t5,1");
      break;
    case 52:
      cmdToDisplay("click t5,1");
      break;
    case 71://输入对话框:工作日志保存
      msgFileName.trim();
      if(!msgFileName.length())return;
      sendActId=1;
      tem=msgFileName + ".txt";
      //sd.remove("WORKLOG.txt");
      SdFile myFile("WORKLOG.txt", FILE_WRITE);
      if (!myFile.rename(sd.vwd(),tem.c_str())){
        Serial.println("WORKLOG RENAME OK !");
      }else{
        Serial.println("WORKLOG RENAME fAIL !");
      }
      sendActId=actIdPre;
      cmdToDisplay("page send");//返回发报界面
      break;
  }
}

//显示字符输入验证,仅对输入的字符本身验证以及长度验证,不验证数值;true通过验证,false验证失败
void checkShow(byte sys,byte chKey,byte chOemKey,int chMod){
  if(chKey>=97 && chKey<=122)chKey-=32;//小写转为大写
  if(chKey==19)sendEnter(sendActId,chMod);//回车处理:发送
  switch(sys){
    case 10://报文区
      //if(wordsTotal>=100)return;//满页不再接受输入
      if(msgForDisplay.length()>=MSGMAXLENGTH)return;
      //空格的处理
      if(chKey==32){
        if(msg.endsWith(" ") || !msg.length())return;//无效空格
        if(sys1==1){//业务类型为电报
           msg=msg + " ";
           msgForDisplay=msgForDisplay + " ";
           charToDisplay(" ");
           if(sys0==1)msgToNano(" ");//当前为随输随发模式,发送
           //wordsTotal++;//更新组数
        }
        return;
      }
      if((chKey>=48 && chKey<=57) || (chKey>=65 && chKey<=90) || chKey==46 || chKey==63 || chKey==33 || chKey==47 || chKey==95){//数字,字母,句号,问号(数码改错),感叹号(混合码改错),“/-”
          msgForDisplay=msgForDisplay+char(chKey);
          charToDisplay(String(char(chKey)));
          if((sys2==1 || sys1>1) && (chKey>=48 && chKey<=57))chKey+=numberLong;//仅为电报短码时数字按短码处理,否则转为长码98->107
          if(sys0) msgToNano(String(char(chKey)));//随输随发模式
          msg=msg+char(chKey);
      }
      break;
    case 1://自由勤务区,不受sys1限制,数字一律按长码发送
      if(freeForScrn.length()>=FREELENGTHMAX) return;//长度验证
      if(chKey==32 || (chKey>=48 && chKey<=57) || (chKey>=65 && chKey<=90) || chKey==63 || chKey==47 || chKey==33 || chKey==45){//空格、数字、字母、问号、!、“/-”
        freeForScrn=freeForScrn+char(chKey);
        charToDisplay(String(char(chKey)));
        if(chKey>=48 && chKey<=57)chKey+=numberLong;
        freeForNano=freeForNano+String(char(chKey));}
      break;
    case 2://号数,受限于电报
      if(sys1!=1)return;//仅电报时可输入
      if(numberForScrn.length()>=8)return;//长度验证
      if(chKey>=48 && chKey<=57){//数字
        numberForNano+=char(chKey);
        numberForScrn+=char(chKey);
        cmdToDisplay("t2.txt=\"" + numberForScrn + "\"");
        //charToDisplay(String(char(chKey)));
        return;}
      if(chKey==47 && numberForScrn.indexOf("/")==-1){
        numberForScrn+=char(chKey);
        numberForNano+=char(chKey);
        cmdToDisplay("t2.txt=\"" + numberForScrn + "\"");
        return;
      }
      if((chKey==69) && numberForScrn.indexOf("E")==-1){
        numberForScrn+=char(chKey);
        numberForNano+=char(chKey);
        cmdToDisplay("t2.txt=\"" + numberForScrn + "\"");
        return;
      }
      if((chKey==88) && numberForScrn.indexOf("X")==-1){
        numberForScrn+=char(chKey);
        numberForNano+=char(chKey);
        cmdToDisplay("t2.txt=\"" + numberForScrn + "\"");
      }
      break;
    case 3://组数,受限于电报
      if(sys1!=1)return;//仅电报时可输入
      if(msgGroup.length()>=5) return;//长度验证
      if(chKey>=48 && chKey<=57){
        msgGroup+=char(chKey);
        cmdToDisplay("t3.txt=\"" + msgGroup + "\"");
      }
      break;
    case 4://等级,受限于电报
      if(sys1!=1)return;//仅电报时可输入
      if(msgClass.length()>=5) return;//长度验证
      if(chKey>=48 && chKey<=57){
        msgClass+=char(chKey);
        cmdToDisplay("t4.txt=\"" + msgClass + "\"");
      }
      break;
    case 5://日期,受限于电报
      if(sys1!=1)return;//仅电报时可输入
      if(msgDate.length()>=4) return;//长度验证
      if(chKey>=48 && chKey<=57){
        msgDate+=char(chKey);
        cmdToDisplay("t5.txt=\"" + msgDate + "\"");
      }
      break;
    case 6://时间,受限于电报
      if(sys1==0 || sys1==4)return;//仅电报时可输入
      if(msgTime.length()>=4) return;//长度验证
      if(chKey>=48 && chKey<=57){
        msgTime+=char(chKey);
        cmdToDisplay("t6.txt=\"" + msgTime + "\"");
      }
      break;
    case 7://号数2,受限于无线电信号,公电
      if(sys1!=2 && sys1!=3 && sys1!=5)return;//通信检查、无线电信号,公电时可输入
      if(numberForScrn.length()>=8) return;//长度验证
      if(chKey>=48 && chKey<=57){
        numberForScrn+=char(chKey);
        numberForNano+=char(chKey);
        cmdToDisplay("t7.txt=\"" + numberForScrn + "\"");
        return;
      }
      if(chKey==47 && numberForScrn.indexOf("/")==-1){
        numberForScrn+=char(chKey);
        numberForNano+=char(chKey);
        cmdToDisplay("t7.txt=\"" + numberForScrn + "\"");
        return;
      }
      if((chKey==69 || chKey==101) && numberForScrn.indexOf("E")==-1){
        numberForScrn+=char(chKey);
        numberForNano+=char(chKey);
        cmdToDisplay("t7.txt=\"" + numberForScrn + "\"");
        return;
      }
      if((chKey==88 || chKey==120) && numberForScrn.indexOf("X")==-1){
        numberForScrn+=char(chKey);
        numberForNano+=char(chKey);
        cmdToDisplay("t7.txt=\"" + numberForScrn + "\"");
      }
      break;
    case 8://时间2,受限于无线电信号,公电
      if(sys1!=2 && sys1!=3 && sys1!=5)return;//通信检查、无线电信号,公电时可输入
      if(msgTime.length()>=4) return;//长度验证
      if(chKey>=48 && chKey<=57){
        msgTime+=char(chKey);
        cmdToDisplay("t8.txt=\"" + msgTime + "\"");
      }
      break;
    case 9://附注,受限于电报,无线电信号,公电
      if(sys1==0 || sys1==4 || sys1==5)return;//仅电报,无线电信号,公电时可输入
      if(msgMarket.length()>=50) return;//长度验证
      if((chKey>=65 && chKey<=90) || (chKey>=48 && chKey<=57) || chKey==47 || chKey==45){//字母、数字、“/”、“-”
        msgMarket+=char(chKey);
        charToDisplay(String(char(chKey)));
        //cmdToDisplay("click btnDisplay,1");
      }
      break;
    case 0://速度
      if(msgSpeed.length()>=3) return;//长度验证
      if(chKey>=48 && chKey<=57){//数字
        msgSpeed+=char(chKey);
        cmdToDisplay("t0.txt=\"" + msgSpeed + "\"");
      }
      break;
    case 51://计算器的第1个数,仅响应数字
      if(chKey>=48 && chKey<=57){//数字
        cmdToDisplay("va0.val=" + String(chKey-48));
        cmdToDisplay("click n0,1");
      }
      break;
    case 52://计算器的第2个数,仅响应数字
      if(chKey>=48 && chKey<=57){//数字
        cmdToDisplay("va0.val=" + String(chKey-48));
        cmdToDisplay("click n1,1");
      }
      break;
    case 71://日志保存输入框
      if(msgFileName.length()>=15) return;//长度验证
      if((chKey>=48 && chKey<=57) || (chKey>=97 && chKey<=122) || (chKey>=65 && chKey<=90)){//数字字母
        msgFileName+=char(chKey);
        cmdToDisplay("t0.txt=\"" + msgFileName + "\"");
      }
      break;
   case 12://报底调用:起始页码
      if(chKey>=48 && chKey<=57){//数字
        long tem;
        tem=dataPage*10+chKey-48;
        if(dataKind==0 && tem<=999){
           dataPage=tem;
           if(dataGroup>(999-tem+1)*100)dataGroup=(999-tem+1)*100;//自动纠正组数
           cmdToDisplay("n1.val=" + String(dataPage));
           cmdToDisplay("n2.val=" + String(dataGroup));
           return;
        }
        if(dataKind>0 && tem<=99){
           dataPage=tem;
           if(dataGroup>(99-tem+1)*100)dataGroup=(99-tem+1)*100;//自动纠正组数
           cmdToDisplay("n1.val=" + String(dataPage));
           cmdToDisplay("n2.val=" + String(dataGroup));
        }
      }
      break;
    case 13://报底调用:调用组数
      if(chKey>=48 && chKey<=57){
        long tem;
        tem=dataGroup*10+chKey-48;
        if((dataKind==0 && (dataPage*100+tem)<=99900) || (dataKind>0 && (dataPage*100+tem)<=9900)){
           dataGroup=tem;
           cmdToDisplay("n2.val="+String(dataGroup));
        }
      }
      break;
  }
}
void keyPressed() {
  byte chKey=keyboard.getKey();//内码
  byte chOemKey=keyboard.getOemKey();//扫描码
  int chMod = keyboard.getModifiers();//修饰符
//Serial.println(chKey);
  if(chKey>0){   
    checkShow(sendActId,chKey,chOemKey,chMod);//输入的显示字符非法,不响应
  }else{
    checkHide(chOemKey,chMod);//输入的隐藏字符非法,不响应
    }
}

void setup() {
  //pinMode(pinDisplayPower, OUTPUT);
  //digitalWrite(pinDisplayPower,HIGH);
  pinMode(pinLedR, OUTPUT);
  digitalWrite(pinLedR,LOW);
  pinMode(pinLedG, OUTPUT);
  digitalWrite(pinLedG,HIGH);
  pinMode(pinLedB, OUTPUT);
  digitalWrite(pinLedB,HIGH);

  Serial.begin(115200);//调试用
  while (!Serial);
  Serial3.begin(115200);//去往Nano
  while (!Serial1);
  Serial1.begin(115200);//去往显示屏
  while (!Serial3);
  Serial.println("due started");

  if (!sd.begin(pinChipSelect, SD_SCK_MHZ(5))) {
    Serial.println("sd failed!");
  }else{
    Serial.println("sd ok...");
    sd.remove("WORKLOG.txt");
    sd.remove("WORKLOG2.txt");
    }
  //系统初始化,让屏幕保持一致
  delay(3000);
  cmdToDisplay("page send");
  cmdToDisplay("sys1=1");//设定工作类型为电报
  cmdToDisplay("sys0=1");//设定发送模式为随输随发
  cmdToDisplay("send.sendActId.val=1");//设定焦点为自由输入区
  cmdToDisplay("tmr0.en=1");//启动光标效果

  digitalWrite(pinLedR,HIGH);
  digitalWrite(pinLedG,LOW);
}

void loop() {
  usb.Task();
  SerialEventP();
}

void SerialEventP(){
  if(!Serial3.available())return;
  String tem="";
  while (Serial3.available()) {
    tem=tem + char(Serial3.read());
    }
  if(tem.length()>1){
     Serial.println("information for save !");
     saveLog(tem);
  }else{
     if(msg.equals("~"))sendDataNextPage();//调用发送模式下,nano请求下一页内容
  }
}
[/mw_shl_code]
 楼主| 发表于 2019-3-10 23:18 | 显示全部楼层
[mw_shl_code=arduino,true]从MCU代码:
//nano端:字符译码、发报

String msg="";//报文内容
byte mcode[10];//字符翻译后的点hua
int eIndexCode=-1;//mcode的最大下标!
float pTime=50;//点持续时间,单位ms
byte wTime=7;//组间隔等于多少个点的持续时间,取值:5,7,用于改变音调
String warnMsg="";//警报台自动呼叫时的内容
bool autoCall=false;//当前是否处于自动呼叫状态,false否,true是
int autoCallDelay=1000;//自动呼叫时,每次发送完自呼被用后延迟的时间

byte cwPin=9;//去往芯片,控制信号脚
byte ePin=8;//去往芯片,使能脚f-+
bool cSave=false;//工作记录标识
String msgSave="";//工作日志内容,达到500个字符后遇空格或无发送内容时,或收到due命令,将日志内容发至due

bool sendMode=false;//标识是否为调用报底模式,true是
bool nextPage=false;//true标识等待下一页报文
//译码核心
boolean MsgCoded(int index){
  char charTranslate=msg.charAt(index);
  msgSave=msgSave + msg.charAt(index);
  msg=msg.substring(1);//去掉将发送的第一个字符
  switch(charTranslate){
    case ' ':
      mcode[0]=0;
      eIndexCode=0;
      break;
    case '1':
      mcode[0]=1;
      mcode[1]=3;
      eIndexCode=1;
      break;
    case '2':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=3;
      eIndexCode=2;
      break;
    case '3':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=3;
      mcode[4]=3;
      eIndexCode=4;
      break;
    case '4':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=3;
      eIndexCode=4;
      break;
    case '5':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '6':
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '7':
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '8':
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      eIndexCode=2;
      break;  
    case '9':
      mcode[0]=3;
      mcode[1]=1;
      eIndexCode=1;
      break;
    case '0':
      mcode[0]=3;
      eIndexCode=0;
      break;
    case 'A':
      mcode[0]=1;
      mcode[1]=3;
      eIndexCode=1;
      break;
    case 'B':
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      eIndexCode=3;
      break;
    case 'C':
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=3;
      mcode[3]=1;
      eIndexCode=3;
      break;
    case 'D':
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      eIndexCode=2;
      break;
    case 'E':
      mcode[0]=1;
      eIndexCode=0;
      break;
    case 'F':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=3;
      mcode[3]=1;
      eIndexCode=3;
      break;
    case 'G':
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=1;
      eIndexCode=2;
      break;
    case 'H':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      eIndexCode=3;
      break;  
    case 'I':
      mcode[0]=1;
      mcode[1]=1;
      eIndexCode=1;
      break;
    case 'J':
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=3;
      mcode[3]=3;
      eIndexCode=3;
      break;
    case 'K':
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=3;
      eIndexCode=2;
      break;  
    case 'L':
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=1;
      mcode[3]=1;
      eIndexCode=3;
      break;
    case 'M':
      mcode[0]=3;
      mcode[1]=3;
      eIndexCode=1;
      break;
    case 'N':
      mcode[0]=3;
      mcode[1]=1;
      eIndexCode=1;
      break;
    case 'O':
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=3;
      eIndexCode=2;
      break;
    case 'P':
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=3;
      mcode[3]=1;
      eIndexCode=3;
      break;
    case 'Q':
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=1;
      mcode[3]=3;
      eIndexCode=3;
      break;
    case 'R':
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=1;
      eIndexCode=2;
      break;
    case 'S':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      eIndexCode=2;
      break;
    case 'T':
      mcode[0]=3;
      eIndexCode=0;
      break;
    case 'U':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=3;
      eIndexCode=2;
      break;
    case 'V':
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=3;
      eIndexCode=3;
      break;  
    case 'W':
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=3;
      eIndexCode=2;
      break;
    case 'X':
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=3;
      eIndexCode=3;
      break;
    case 'Y':
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=3;
      mcode[3]=3;
      eIndexCode=3;
      break;
    case 'Z':
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=1;
      mcode[3]=1;
      eIndexCode=3;
      break;
    case '!'://0
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=3;
      mcode[3]=3;
      mcode[4]=3;
      eIndexCode=4;
      break;
    case '"'://1
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=3;
      mcode[3]=3;
      mcode[4]=3;
      eIndexCode=4;
      break;
    case '#'://2
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=3;
      mcode[3]=3;
      mcode[4]=3;
      eIndexCode=4;
      break;
    case '$'://3
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=3;
      mcode[4]=3;
      eIndexCode=4;
      break;
    case '%'://4
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=3;
      eIndexCode=4;
      break;
    case '&'://5
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '\''://6
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '('://7
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case ')'://8
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=3;
      mcode[3]=1;
      mcode[4]=1;
      eIndexCode=4;
      break;  
    case '*'://9
      mcode[0]=3;
      mcode[1]=3;
      mcode[2]=3;
      mcode[3]=3;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '/'://斜线
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=3;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '?'://数码的改错
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=3;
      mcode[3]=3;
      mcode[4]=1;
      mcode[5]=1;
      eIndexCode=5;
      break;
    case '|'://字码、混合码的改错
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      mcode[5]=1;
      mcode[6]=1;
      mcode[7]=1;
      eIndexCode=7;
      break;
    case '{'://分段,开始
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=3;
      eIndexCode=4;
      break;
    case '}'://句号
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      mcode[5]=1;
      eIndexCode=5;
      break;
    case '@'://完结
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=1;
      mcode[3]=3;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '^'://无线电信号^
      mcode[0]=1;
      mcode[1]=1;
      mcode[2]=3;
      mcode[3]=1;
      mcode[4]=1;
      eIndexCode=4;
      break;
    case '.'://小数点
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=1;
      mcode[3]=3;
      mcode[4]=1;
      mcode[5]=3;
      eIndexCode=5;
      break;   
      case '<'://括号
      mcode[0]=1;
      mcode[1]=3;
      mcode[2]=1;
      mcode[3]=3;
      mcode[4]=1;
      mcode[5]=3;
      eIndexCode=5;
      break;   
    case '-'://页标
      mcode[0]=3;
      mcode[1]=1;
      mcode[2]=1;
      mcode[3]=1;
      mcode[4]=1;
      mcode[5]=3;
      eIndexCode=5;
      break;
    default:
      return false;
    }
  return true;
}
//发码核心
void sendMorse(){
  if(msgSave.length()>=500){//.保存的内容已有一页,先给due
        Serial.print(msgSave);
        msgSave="";
      }

  if(!msg.length()){
    if(autoCall){//警报台自动呼叫
      delay(autoCallDelay);
      msg=warnMsg;
    }else{
      sendMode=false;
      delay(500);return;
    }//无报文等待发送
  }else{
    if(sendMode && msg.length()<=5)if(nextPage)Serial.write(126);//126波浪号,即将发送完,通知due发下一页
  }
  if(!MsgCoded(0))return;//译码失败

  if(mcode[0]==0){delay(pTime*(wTime-3));return;}//当前字符为组间隔,-3因为上一字符的码间隔已经占用了3个点的时间

  //有效报文发报
  for(byte i=0;i<eIndexCode;i++){//一次性发送当前字符
    digitalWrite(cwPin,LOW);//打开模拟开关
    delay(pTime*mcode);//point or dash 持续时间
    digitalWrite(cwPin,HIGH);//关闭模拟开关
    delay(pTime);//thi time between point and dash
    }
  digitalWrite(cwPin,LOW);//发码的最后一个点(hua)
  delay(pTime*mcode[eIndexCode]);
  digitalWrite(cwPin,HIGH);//码间隔
  delay(pTime*3);  
}

void cmdPro(byte cmd){
  byte inChar=0;
  switch(cmd){
    case 240://发报速度调整
      inChar = Serial.read();
      if(inChar<60 || inChar>250)return;
      pTime= 50*100/inChar;
      break;
    case 242://警报台自动呼叫开始命令
      warnMsg="";
      inChar=Serial.read();
      while (Serial.available()) {
      warnMsg=warnMsg + String(char(inChar));
      inChar=Serial.read();}
      if(warnMsg.length()<1 || inChar!=242)return;
      msg=warnMsg + String(char(inChar));
      autoCall=true;
      autoCallDelay=int(pTime*20);
      break;
    case 243://警报台自动呼叫停止命令
      msg="";
      autoCall=false;
      break;
    case 246://开始记录发报内容
      cSave=true;
      msgSave="";
      break;
    case 247://停止记录发报内容
      cSave=false;
      msgSave="";
      break;
    case 248://发送记录的内容到due
      Serial.print(msgSave);
      msgSave="";
      break;
    case 250://通知Nano停止发报,并将各参数复位
      sendMode=false;
      nextPage=false;
      msg="";
      autoCall=false;
      warnMsg="";
      break;
    case 251://开始调用报底自动发送模式,每次发一页,余5个字符时nano请求due发一下页
      sendMode=true;
      nextPage=true;
      msg="";
      break;
    case 252://结束调用报底自动发送模式;
      nextPage=false;
      break;
    }
}
void setup()
{
  pinMode(cwPin, OUTPUT);
  pinMode(ePin, OUTPUT);
  digitalWrite(ePin,HIGH);
  digitalWrite(cwPin,HIGH);
  Serial.begin(115200);
  while (!Serial);
  //Serial.println("nano started");
  digitalWrite(ePin,LOW);
}

void loop()
{  
  sendMorse();
}
void serialEvent() {
  if(autoCall){
    msg="";
    autoCall=false;//停止警报台自动呼叫
    nextPage=false;//停止调用报底发报
  }
  while (Serial.available()) {
    byte inChar = Serial.read();
    if(inChar<220){//发送的符号
      msg=msg + char(inChar);
    }else{//来自due的命令
      cmdPro(inChar);
    }
  }  
}
[/mw_shl_code]
 楼主| 发表于 2019-3-11 00:38 | 显示全部楼层
自己的沙发自己坐
 楼主| 发表于 2019-4-25 20:18 | 显示全部楼层
最近进行了升级,V1.0版正式诞生,4月24日进行了实际工作检测,证明可以用于工作,具体无法细说,领导不让,仅贴出最新的外形图,有意可私下共同研究。最后一张为arduino due开发板和我焊接的母板,母板上的东西都扣在了下面,以节省空间。
IMG_20190425_193530.jpg IMG_20190425_193734.jpg IMG_20190425_193547.jpg IMG_20190425_193552.jpg IMG_20190424_001031.jpg
 楼主| 发表于 2019-4-25 23:03 | 显示全部楼层
链接:https://pan.baidu.com/s/1Ryeci9kmw_unnfQsE4_OBQ
提取码:47fc
复制这段内容后打开百度网盘手机App,操作更方便哦
有问题可联系我QQ:2554276567
发表于 2019-5-31 01:11 | 显示全部楼层
创意不错,比较有前途!请加好友2363395579
发表于 2019-5-31 06:37 来自手机 | 显示全部楼层
感谢大神的分享,收获匪浅!加油!
 楼主| 发表于 2019-6-13 18:59 | 显示全部楼层
写了文章
申请了专利
希望能拿个大奖
发表于 2019-6-18 17:48 | 显示全部楼层
666不错不错
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-12-30 00:51 , Processed in 0.197325 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表