第3章 自助饮料机项目设计 本章根据 潘辉江、曾致远 项目设计整理而成。 3.1项目背景 在日常生活中常常能看到自动售货机,却很少看到自助饮料机。通常情况下,饮料需要特定的店铺或水吧等有专门售货员的场所才能提供,既不方便客户,也占用了比较多的场地和人力资源。因此,本项目设计一款可以定量提供饮料、支持线上支付的自助饮料机。 3.2创新描述 用户可以通过线上支付,使用自己的水杯或者饮料机提供一次性水杯,从饮料机接一杯饮料(200~300ml)。实验中通过计算机发送指令,经WiFi模块处理,达到控制饮料机的目的。 用户需要做的只是完成安全支付,然后使用饮料机的按键(触摸传感器)或者通过网页单击“出水”按钮,即可对饮料机进行操作,用水杯完成接水动作。饮料机的维护相对简单,每天剩余的饮料进行处理并将饮料机的储水箱加满即可。 3.3功能及总体设计 本项目主要分两部分进行设计: 实体执行部分和WiFi模块执行部分。实体执行部分完成饮料机的基本功能,通过开关按钮、水位传感器和触摸传感器完成定量出水的功能; WiFi模块执行部分实现Arduino开发板与其他设备在同一热点下的信息传输。 3.3.1功能介绍 本项目设计的饮料机通过WiFi传输数据,对饮料机进行无线控制且控制定量出水,既节省了人力,又可以避免用户过量接水等现实问题。 3.3.2总体设计 实现上述功能需要将作品分三部分进行设计: 出水开关部分、分层传感器部分和WiFi模块控制部分。出水开关部分采用直流电机和齿轮组,控制出水口的开关; 分层传感器部分为两个水位传感器的过渡层,通过水位的变化决定上下开关的状态; WiFi模块控制部分使用ESP826601模块,在STA模式下与计算机连接同一个服务器,实现物联网的功能,完成WiFi数据传输。 3个功能模块最后集成于Arduino开发板,通过对ESP826601模块的调试和Arduino开发板的编程实现预期功能。实体部分简易结构示意图如图31所示。 图31实体部分简易结构示意图 1. 整体框架图 整体框架如图32所示。 图32整体框架图 2. 系统流程图 系统流程图如图33所示。 图33系统流程图 接通电源以后,装置进入待机模式。若接收到触摸传感器或WiFi模块开始工作的信号,则主程序开始工作,判断水位是否高于注水水位,若是则继续出水,若否则进入注水模式。判断水位是否高于待机水位,若是则继续注水; 若否则进入待机模式。至此,装置的一个工作流程执行完毕,若有信号将循环执行。 3. 总电路图 系统总电路如图34所示,元件间的连线如表31所示。 图34总电路图 表31元件间的连线 元件引脚Arduino开发板引脚 水位传感器 VCC5V SA0和A1 GNDGND 触摸传感器 VCC5V S2 GNDGND LED +3、7和8 -GND 300直流电机(上层电机) +6 -5 300直流电机(下层电机) +10 -9 ESP826601 VCC3.3V GNDGND CH_PD3.3V RX1 TX0 续表 元件引脚Arduino开发板引脚 LED发光二极管 +4 -GND 如图34所示,本电路用到2个水位传感器、1个触摸传感器、2个直流电机以及4个LED。其中,后3个LED为状态指示灯,表示饮料机当前的工作状态。电路中用到1个WiFi模块(ESP826601),完成Arduino开发板与计算机等其他设备的通信功能。 3.3.3模块介绍 本项目主要包括实体执行模块和WiFi两个模块。下面分别给出各模块的功能和相关代码。 1. 实体执行模块 本部分包括功能介绍和相关代码。 1) 功能介绍 设置引脚,将触摸传感器作为输入,以启动饮料机进入工作状态。元件包括2个水位传感器、2个直流电机、触摸传感器、3个LED、3个电阻(2kΩ)以及Arduino开发板。实体执行模块如图35所示,元件间的连线如表32所示。 图35实体执行模块 表32元件间的连线(实体执行模块) 元件引脚Arduino开发板引脚 水位传感器 VCC5V SA0和A1 GNDGND 触摸传感器 VCC5V S2 GNDGND LED +3、7和8 -GND 300直流电机(上层电机) +6 -5 300直流电机(下层电机) +10 -9 2) 相关代码 double temp1,data1; //下层水位传感器 double temp2,data2; //上层水位传感器 int enginAbovePo=5; //上层电机——正 int enginAboveNe=6; //上层电机——负 int enginBelowPo2=11; //下层副电机——正 int enginBelowNe2=12; //下层副电机——负 int enginBelowPo=9; //下层电机——正 int enginBelowNe=10; //下层电机——负 int buttonpin=2; //触摸传感器输入 int ledyellow=3; //黄灯待机 int ledred=7; //红灯工作 int ledblue=8; //蓝灯调整 int WatersensorAbove=1; //待机水位传感器 int WatersensorBelow=0; //注水水位传感器 int WifiOnoff = 0; //WiFi信号开关 int val; //触摸传感器开关 void SetStandby(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne); void SetWorking(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne); void SetAdjust(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne); void KeepStandby(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne); void KeepWorking(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne,int Watersensor); void KeepAdjust(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne,int Watersensor); void setup() { pinMode(enginAbovePo,OUTPUT); pinMode(enginAboveNe,OUTPUT); pinMode(enginBelowPo,OUTPUT); pinMode(enginBelowNe,OUTPUT); pinMode(ledred,OUTPUT); pinMode(ledblue,OUTPUT); pinMode(ledyellow,OUTPUT); pinMode(WiFi_LED,OUTPUT) pinMode(buttonpin,INPUT); Serial.begin(115200); delay(5000); } void loop() { val=digitalRead(buttonpin); //将触摸传感器状态赋给val if(val == LOW && WifiOnoff == 0)//触摸传感器无信号,黄灯亮(待机) { KeepStandby(enginBelowPo,enginBelowNe,enginAbovePo,enginAboveNe); } else //有信号,开始工作 { temp1=(long)analogRead(0); data1=(temp1/650)*4; Serial.print("the below orginal depth is:"); Serial.print(data1); Serial.println("cm"); Serial.println("waterflooding started"); SetWorking(enginBelowPo,enginBelowNe,enginAbovePo,enginAboveNe); delay(100); KeepWorking(enginBelowPo,enginBelowNe,enginAbovePo,enginAboveNe,WatersensorBelow); Serial.println("statement transformed"); temp1=(long)analogRead(0); data1=(temp1/650)*4; if(data1<2.2) { SetAdjust(enginBelowPo,enginBelowNe,enginAbovePo,enginAboveNe); //水位低,蓝灯亮,红灯灭(补水) //关闭用户出水口,开启内部注水口 Serial.println("water level low"); delay(100); KeepAdjust(enginBelowPo,enginBelowNe,enginAbovePo,enginAboveNe,WatersensorAbove); temp2=(long)analogRead(1); data2=(temp2/650)*4; if(data2>=1.8)//水位高,黄灯亮,蓝灯灭(待机) { SetStandby(enginBelowPo,enginBelowNe,enginAbovePo,enginAboveNe); //关闭内部注水口,待机等待下一位用户 Serial.print("waterflooding completed"); } } } WifiOnoff = 0; //WiFi开关置0 } //实体执行部分控制函数 void SetStandby(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne) //直流电机1为下层电机,直流电机2为上层电机 { analogWrite(engin1Po,255); analogWrite(engin1Ne,0); digitalWrite(enginBelowPo2,HIGH); digitalWrite(enginBelowNe2,LOW); analogWrite(engin2Po,255); analogWrite(engin2Ne,0); digitalWrite(ledyellow,HIGH); digitalWrite(ledblue,LOW); digitalWrite(ledred,LOW); delay(500); } void SetWorking(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne) { analogWrite(engin1Po,255); analogWrite(engin1Ne,0); digitalWrite(enginBelowPo2,HIGH); digitalWrite(enginBelowNe2,LOW); analogWrite(engin2Po,0); analogWrite(engin2Ne,0); digitalWrite(ledred,HIGH); digitalWrite(ledyellow,LOW); digitalWrite(ledblue,LOW); } void SetAdjust(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne) { analogWrite(engin1Po,0); analogWrite(engin1Ne,255); digitalWrite(enginBelowPo2,LOW); digitalWrite(enginBelowNe2,HIGH); analogWrite(engin2Po,255); analogWrite(engin2Ne,0); digitalWrite(ledblue,HIGH); digitalWrite(ledred,LOW); digitalWrite(ledyellow,LOW); } void KeepStandby(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne) { analogWrite(engin1Po,0); analogWrite(engin1Ne,0); analogWrite(engin2Po,0); analogWrite(engin2Ne,0); digitalWrite(enginBelowPo,LOW); digitalWrite(enginBelowNe,LOW); digitalWrite(ledyellow,HIGH); digitalWrite(ledred,LOW); digitalWrite(ledblue,LOW); } void KeepWorking(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne,int Watersensor) { double temp,data; for(temp=(long)analogRead(Watersensor),data=(temp/650*4); data>2.12; temp=(long)analogRead(Watersensor),data=(temp/650*4) ) //判断水位是否达到注水水位(内部注水,蓝灯亮;外部注水,红灯亮) { SetWorking(engin1Po,engin1Ne,engin2Po,engin2Ne); Serial.print("the depth is:"); Serial.print(data); Serial.println("cm"); } } void KeepAdjust(int engin1Po,int engin1Ne,int engin2Po,int engin2Ne,int Watersensor) { double temp,data; for(temp=(long)analogRead(Watersensor),data=(temp/650*4); data<1.8; temp=(long)analogRead(Watersensor),data=(temp/650*4) ) //判断水位是否达到待机水位(结束注水,黄灯亮;继续注水,蓝灯亮) { SetAdjust(engin1Po,engin1Ne,engin2Po,engin2Ne); Serial.print("the depth is:"); Serial.print(data); Serial.println("cm"); } } 2. WiFi模块 本部分包括功能介绍、具体实现和相关代码。 1) 功能介绍 基于局域网的拓扑结构,将WiFi模块和其他设备作为站点(Station),组建成一个网络。该网络中所有的站点均可相互通信,在本实验中的体现是计算机和ESP826601之间的通信,经ESP826601向Arduino开发板发送指令,可控制设备主体的一系列行为。WiFi模块电路如图36所示,模块连线如表33所示。 图36WiFi模块电路图 表33WiFi模块的连线 元件引脚Arduino开发板引脚 ESP826601 VCC3.3V GNDGND CH_PD3.3V RX1 TX0 LED发光二极管 +4 -GND 2) 具体实现 在设计中加入WiFi模块以实现无线数据传输。首先,需要通过串口调试器sscom42(如图37所示)进行一系列的AT指令调试,并通过网络调试助手(如图38所示)模拟服务器,实现串口调试器与模拟客户端的通信。 图37串口调试器界面 图38网络调试助手界面 常用AT指令及其功能如下: (1) AT+RST,重置; (2) AT+CWMODE=1,设置客户端模式; (3) AT+CIPMUX=0,开启单连接状态; (4) AT+CWJAP="onem2m","ts9g36v5eplk",此处填写WiFi信号的ID和密码; (5) AT+CIPSTART="TCP","192.168.1.105",8080,此处填写模拟客户端的IP和端口; (6) AT+CIPMODE=1,开启穿透传输模式; (7) AT+CIPSEND,进行数据传输; (8) AT+CIPCLOSE,停止数据传输。 调试完毕后,WiFi模块具备了数据传输的功能,借助物联网云平台——贝壳物联网提供的头文件搭建的平台,并适当修改代码,即可使其满足所需要的功能。当设备接通电源后,将会在贝壳物联网的设备界面显示在线,此时输入预设的指令即可完成无线控制,如图39和图310所示。 图39智能设备界面 图310平台显示界面 3) 相关代码 //=================WiFi模块控制部分=============================== #include //============= 此处必须修改============ String DEVICEID="2348"; //设备编号 String APIKEY="4490217d4"; //设备密码 //======================================= int Wifi_LED = 4; //WiFi控制信号灯 unsigned long lastCheckInTime = 0; //记录上次报到的时间 const unsigned long postingInterval = 40000; //每40s向服务器报到一次 String inputString = ""; boolean stringComplete = false; boolean CONNECT = true; char* parseJson(char *jsonString); void checkIn(); int processMessage(aJsonObject *msg); void sayToClient(String client_id, String content); void serialEvent(); //==================================================// void setup() { pinMode(enginAbovePo,OUTPUT); pinMode(enginAboveNe,OUTPUT); pinMode(enginBelowPo,OUTPUT); pinMode(enginBelowNe,OUTPUT); pinMode(ledred,OUTPUT); pinMode(ledblue,OUTPUT); pinMode(ledyellow,OUTPUT); pinMode(WiFi_LED,OUTPUT) pinMode(buttonpin,INPUT); Serial.begin(115200); delay(5000); } void loop() { //============== WiFi模块执行部分 ============// if(millis() - lastCheckInTime > postingInterval || lastCheckInTime==0) { checkIn(); } serialEvent(); if (stringComplete) { inputString.trim(); //Serial.println(inputString); if(inputString=="CLOSED") { Serial.println("connect closed!"); CONNECT=false; } else { int len = inputString.length()+1; if(inputString.startsWith("{") && inputString.endsWith("}")) { char jsonString[len]; inputString.toCharArray(jsonString,len); aJsonObject *msg = aJson.parse(jsonString); WifiOnoff = processMessage(msg); //clear the string: inputString = ""; stringComplete = false; } } } //=============== WiFi执行部分结束 =================// //===============实体执行部分====================// WifiOnoff = 0; //WiFi开关置0 } //=========================WiFi模块控制函数===========================// void checkIn() { if (!CONNECT) { Serial.print("+++"); delay(500); Serial.print("\r\n"); delay(1000); Serial.print("AT+RST\r\n"); delay(6000); CONNECT=true; lastCheckInTime=0; } else { Serial.print("{\"M\":\"checkin\",\"ID\":\""); Serial.print(DEVICEID); Serial.print("\",\"K\":\""); Serial.print(APIKEY); Serial.print("\"}\r\n"); lastCheckInTime = millis(); } } int processMessage(aJsonObject *msg) { aJsonObject* method = aJson.getObjectItem(msg, "M"); aJsonObject* content = aJson.getObjectItem(msg, "C"); aJsonObject* client_id = aJson.getObjectItem(msg, "ID"); //char* st = aJson.print(msg); if (!method) { return 0; } //Serial.println(st); //free(st); String M=method->valuestring; String C=content->valuestring; String F_C_ID=client_id->valuestring; if(M=="say") { if(C=="play") { digitalWrite(Wifi_LED,HIGH); sayToClient(F_C_ID,"LED on!"); return 1; } } } void sayToClient(String client_id, String content) { Serial.print("{\"M\":\"say\",\"ID\":\""); Serial.print(client_id); Serial.print("\",\"C\":\""); Serial.print(content); Serial.print("\"}\r\n"); lastCheckInTime = millis(); } void serialEvent() { while (Serial.available()) { char inChar = (char)Serial.read(); inputString += inChar; if (inChar == '\n') { stringComplete = true; } } } //====头文件"ajson.h"=======// #ifndef aJson__h #define aJson__h #include #include #include #include //进入Arduino millis()函数 /********************************************************************* 定义 ********************************************************************/ //aJson 类型: #define aJson_NULL 0 #define aJson_Boolean 1 #define aJson_Int 2 #define aJson_Float 3 #define aJson_String 4 #define aJson_Array 5 #define aJson_Object 6 #define aJson_IsReference 128 #ifndef EOF #define EOF -1 #endif #define PRINT_BUFFER_LEN 256 //aJson结构体 typedef struct aJsonObject { char *name; struct aJsonObject *next, *prev; struct aJsonObject *child; char type; union { char *valuestring; char valuebool; int valueint; double valuefloat; }; } aJsonObject; class aJsonStream : public Print { public: aJsonStream(Stream *stream_) : stream_obj(stream_), bucket(EOF) {} virtual bool available(); int parseNumber(aJsonObject *item); int printInt(aJsonObject *item); int printFloat(aJsonObject *item); int parseString(aJsonObject *item); int printStringPtr(const char *str); int printString(aJsonObject *item); int skip(); int flush(); int parseValue(aJsonObject *item, char** filter); int printValue(aJsonObject *item); int parseArray(aJsonObject *item, char** filter); int printArray(aJsonObject *item); int parseObject(aJsonObject *item, char** filter); int printObject(aJsonObject *item); protected: virtual int getch(); virtual size_t readBytes(uint8_t *buffer, size_t len); virtual void ungetch(char ch); virtual size_t write(uint8_t ch); Stream *stream_obj; virtual inline Stream *stream() { return stream_obj; } int bucket; }; class aJsonClientStream : public aJsonStream { public: aJsonClientStream(Client *stream_) : aJsonStream(NULL), client_obj(stream_) {} private: virtual int getch(); Client *client_obj; virtual inline Client *stream() { return client_obj; } }; class aJsonStringStream : public aJsonStream { public: aJsonStringStream(char *inbuf_, char *outbuf_ = NULL, size_t outbuf_len_ = 0) : aJsonStream(NULL), inbuf(inbuf_), outbuf(outbuf_), outbuf_len(outbuf_len_) { inbuf_len = inbuf ?strlen(inbuf) : 0; } virtual bool available(); private: virtual int getch(); virtual size_t write(uint8_t ch); char *inbuf, *outbuf; size_t inbuf_len, outbuf_len; }; class aJsonClass { public: aJsonObject* parse(aJsonStream* stream); aJsonObject* parse(aJsonStream* stream,char** filter_values); aJsonObject* parse(char *value); int print(aJsonObject *item, aJsonStream* stream); char* print(aJsonObject* item); char stream(aJsonObject *item, aJsonStream* stream); void deleteItem(aJsonObject *c); unsigned char getArraySize(aJsonObject *array); aJsonObject* getArrayItem(aJsonObject *array, unsigned char item); aJsonObject* getObjectItem(aJsonObject *object, const char *string); aJsonObject* createNull(); aJsonObject* createItem(bool b); aJsonObject* createItem(char b); aJsonObject* createItem(int num); aJsonObject* createItem(double num); aJsonObject* createItem(const char *string); aJsonObject* createArray(); aJsonObject* createObject(); aJsonObject* createIntArray(int *numbers, unsigned char count); aJsonObject* createFloatArray(double *numbers, unsigned char count); aJsonObject* createDoubleArray(double *numbers, unsigned char count); aJsonObject* createStringArray(const char **strings, unsigned char count); void addItemToArray(aJsonObject *array, aJsonObject *item); void addItemToObject(aJsonObject *object, const char *string, aJsonObject *item); void addItemReferenceToArray(aJsonObject *array, aJsonObject *item); void addItemReferenceToObject(aJsonObject *object, const char *string, aJsonObject *item); aJsonObject* detachItemFromArray(aJsonObject *array, unsigned char which); void deleteItemFromArray(aJsonObject *array, unsigned char which); aJsonObject* detachItemFromObject(aJsonObject *object, const char *string); void deleteItemFromObject(aJsonObject *object, const char *string); void replaceItemInArray(aJsonObject *array, unsigned char which, aJsonObject *newitem); void replaceItemInObject(aJsonObject *object, const char *string, aJsonObject *newitem); void addNullToObject(aJsonObject* object, const char* name); void addBooleanToObject(aJsonObject* object, const char* name, bool b); void addNumberToObject(aJsonObject* object, const char* name, int n); void addNumberToObject(aJsonObject* object, const char* name, double n); void addStringToObject(aJsonObject* object, const char* name, const char* s); protected: friend class aJsonStream; static aJsonObject* newItem(); private: void suffixObject(aJsonObject *prev, aJsonObject *item); aJsonObject* createReference(aJsonObject *item); }; extern aJsonClass aJson; #endif //==实现文件"ajson.cpp"=======// #include #include #include #include #include #ifdef __AVR__ #include #else #include #endif #include "aJSON.h" #include "utility/stringbuffer.h" //定义 #define BUFFER_DEFAULT_SIZE 4 #define FLOAT_PRECISION 5 bool aJsonStream::available() { if (bucket != EOF) return true; while (stream()->available()) { int ch = this->getch(); if (ch > 32) { this->ungetch(ch); return true; } } return false; } int aJsonStream::getch() { if (bucket != EOF) { int ret = bucket; bucket = EOF; return ret; } unsigned long i= millis()+500; while ((!stream()->available()) && (millis() < i)) return stream()->read(); } void aJsonStream::ungetch(char ch) { bucket = ch; } size_t aJsonStream::write(uint8_t ch) { return stream()->write(ch); } size_t aJsonStream::readBytes(uint8_t *buffer, size_t len) { for (size_t i = 0; i < len; i++) { int ch = this->getch(); if (ch == EOF) { return i; } buffer[i] = ch; } return len; } int aJsonClientStream::getch() { if (bucket != EOF) { int ret = bucket; bucket = EOF; return ret; } while (!stream()->available() && stream()->connected()) if (!stream()->available()) { stream()->stop(); return EOF; } return stream()->read(); } bool aJsonStringStream::available() { if (bucket != EOF) return true; return inbuf_len > 0; } int aJsonStringStream::getch() { if (bucket != EOF) { int ret = bucket; bucket = EOF; return ret; } if (!inbuf || !inbuf_len) { return EOF; } char ch = *inbuf++; inbuf_len--; return ch; } size_t aJsonStringStream::write(uint8_t ch) { if (!outbuf || outbuf_len <= 1) { return 0; } *outbuf++ = ch; outbuf_len--; *outbuf = 0; return 1; } aJsonObject* aJsonClass::newItem() { aJsonObject* node = (aJsonObject*) malloc(sizeof(aJsonObject)); if (node) memset(node, 0, sizeof(aJsonObject)); return node; } void aJsonClass::deleteItem(aJsonObject *c) { aJsonObject *next; while (c) { next = c->next; if (!(c->type & aJson_IsReference) && c->child) { deleteItem(c->child); } if ((c->type == aJson_String) && c->valuestring) { free(c->valuestring); } if (c->name) { free(c->name); } free(c); c = next; } } int aJsonStream::parseNumber(aJsonObject *item) { int i = 0; int sign = 1; int in = this->getch(); if (in == EOF) { return EOF; } if (in == '-') { sign = -1; in = this->getch(); if (in == EOF) { return EOF; } } if (in >= '0' && in <= '9') do { i = (i * 10) + (in - '0'); in = this->getch(); } while (in >= '0' && in <= '9'); if (!(in == '.' || in == 'e' || in == 'E')) { item->valueint = i * (int) sign; item->type = aJson_Int; } else { double n = (double) i; int scale = 0; int subscale = 0; char signsubscale = 1; if (in == '.') { in = this->getch(); do { n = (n * 10.0) + (in - '0'), scale--; in = this->getch(); } while (in >= '0' && in <= '9'); } if (in == 'e' || in == 'E') { in = this->getch(); if (in == '+') { in = this->getch(); } else if (in == '-') { signsubscale = -1; in = this->getch(); } while (in >= '0' && in <= '9') { subscale = (subscale * 10) + (in - '0'); in = this->getch(); } } n = sign * n * pow(10.0, ((double) scale + (double) subscale * (double) signsubscale)); item->valuefloat = n; item->type = aJson_Float; } this->ungetch(in); return 0; } int aJsonStream::printInt(aJsonObject *item) { if (item != NULL) { return this->print(item->valueint, DEC); } return 0; } int aJsonStream::printFloat(aJsonObject *item) { if (item != NULL) { double d = item->valuefloat; if (d<0.0) { this->print("-"); d=-d; } unsigned long integer_number = (unsigned long)d; this->print(integer_number, DEC); this->print("."); double fractional_part = d - ((double)integer_number); int n = FLOAT_PRECISION; fractional_part += 0.5/pow(10.0, FLOAT_PRECISION); do { fractional_part *= 10.0; unsigned int digit = (unsigned int) fractional_part; this->print(digit, DEC); fractional_part -= (double)digit; n--; } while ((fractional_part!=0) && (n>0)); } return 0; } int aJsonStream::parseString(aJsonObject *item) { int in = this->getch(); if (in != '\"') { return EOF; } item->type = aJson_String; string_buffer* buffer = stringBufferCreate(); if (buffer == NULL) { return EOF; } in = this->getch(); if (in == EOF) { stringBufferFree(buffer); return EOF; } while (in != EOF) { while (in != '\"' && in >= 32) { if (in != '\\') { stringBufferAdd((char) in, buffer); } else { in = this->getch(); if (in == EOF) { stringBufferFree(buffer); return EOF; } switch (in) { case '\\': stringBufferAdd('\\', buffer); break; case '\"': stringBufferAdd('\"', buffer); break; case '/': stringBufferAdd('/', buffer); break; case 'b': stringBufferAdd('\b', buffer); break; case 'f': stringBufferAdd('\f', buffer); break; case 'n': stringBufferAdd('\n', buffer); break; case 'r': stringBufferAdd('\r', buffer); break; case 't': stringBufferAdd('\t', buffer); break; default: break; } } in = this->getch(); if (in == EOF) { stringBufferFree(buffer); return EOF; } } item->valuestring = stringBufferToString(buffer); return 0; } return 0; } int aJsonStream::printStringPtr(const char *str) { this->print("\""); char* ptr = (char*) str; if (ptr != NULL) { while (*ptr != 0) { if ((unsigned char) *ptr > 31 && *ptr != '\"' && *ptr != '\\') { this->print(*ptr); ptr++; } else { this->print('\\'); switch (*ptr++) { case '\\': this->print('\\'); break; case '\"': this->print('\"'); break; case '/': this->print('/'); break; case '\b': this->print('b'); break; case '\f': this->print('f'); break; case '\n': this->print('n'); break; case '\r': this->print('r'); break; case '\t': this->print('t'); break; default: break; } } } } this->print('\"'); return 0; } int aJsonStream::printString(aJsonObject *item) { return this->printStringPtr(item->valuestring); } int aJsonStream::skip() { int in = this->getch(); while (in != EOF && (in <= 32)) { in = this->getch(); } if (in != EOF) { this->ungetch(in); return 0; } return EOF; } int aJsonStream::flush() { int in = this->getch(); while(in != EOF) { in = this->getch(); } return EOF; } aJsonObject* aJsonClass::parse(char *value) { aJsonStringStream stringStream(value, NULL); aJsonObject* result = parse(&stringStream); return result; } aJsonObject* aJsonClass::parse(aJsonStream* stream) { return parse(stream, NULL); } aJsonObject* aJsonClass::parse(aJsonStream* stream, char** filter) { if (stream == NULL) { return NULL; } aJsonObject *c = newItem(); if (!c) return NULL; stream->skip(); if (stream->parseValue(c, filter) == EOF) { deleteItem(c); return NULL; } return c; } int aJsonClass::print(aJsonObject* item, aJsonStream* stream) { return stream->printValue(item); } char* aJsonClass::print(aJsonObject* item) { char* outBuf = (char*) malloc(PRINT_BUFFER_LEN); if (outBuf == NULL) { return NULL; } aJsonStringStream stringStream(NULL, outBuf, PRINT_BUFFER_LEN); print(item, &stringStream); return outBuf; } int aJsonStream::parseValue(aJsonObject *item, char** filter) { if (this->skip() == EOF) { return EOF; } int in = this->getch(); if (in == EOF) { return EOF; } this->ungetch(in); if (in == '\"') { return this->parseString(item); } else if (in == '-' || (in >= '0' && in <= '9')) { return this->parseNumber(item); } else if (in == '[') { return this->parseArray(item, filter); } else if (in == '{') { return this->parseObject(item, filter); } else if (in == 'n') { char buffer[] = { 0, 0, 0, 0 }; if (this->readBytes((uint8_t*) buffer, 4) != 4) { return EOF; } if (!strncmp(buffer, "null", 4)) { item->type = aJson_NULL; return 0; } else { return EOF; } } else if (in == 'f') { char buffer[] = { 0, 0, 0, 0, 0 }; if (this->readBytes((uint8_t*) buffer, 5) != 5) { return EOF; } if (!strncmp(buffer, "false", 5)) { item->type = aJson_Boolean; item->valuebool = false; return 0; } } else if (in == 't') { //读取该值的缓冲区 char buffer[] = { 0, 0, 0, 0 }; if (this->readBytes((uint8_t*) buffer, 4) != 4) { return EOF; } if (!strncmp(buffer, "true", 4)) { item->type = aJson_Boolean; item->valuebool = true; return 0; } } return EOF; } int aJsonStream::printValue(aJsonObject *item) { int result = 0; if (item == NULL) { //无操作 return 0; } switch (item->type) { case aJson_NULL: result = this->print("null"); break; case aJson_Boolean: if(item->valuebool){ result = this->print("true"); } else{ result = this->print("false"); } break; case aJson_Int: result = this->printInt(item); break; case aJson_Float: result = this->printFloat(item); break; case aJson_String: result = this->printString(item); break; case aJson_Array: result = this->printArray(item); break; case aJson_Object: result = this->printObject(item); break; } return result; } int aJsonStream::parseArray(aJsonObject *item, char** filter) { int in = this->getch(); if (in != '[') { return EOF; } item->type = aJson_Array; this->skip(); in = this->getch(); //检查是否为空数组 if (in == ']') { return 0; } this->ungetch(in); aJsonObject *child; char first = -1; while ((first) || (in == ',')) { aJsonObject *new_item = aJsonClass::newItem(); if (new_item == NULL) { return EOF; } if (first) { item->child = new_item; first = 0; } else { child->next = new_item; new_item->prev = child; } child = new_item; this->skip(); if (this->parseValue(child, filter)) { return EOF; } this->skip(); in = this->getch(); } if (in == ']') { return 0; //数组末尾 } else { return EOF; } } int aJsonStream::printArray(aJsonObject *item) { if (item == NULL) { //无操作 return 0; } aJsonObject *child = item->child; if (this->print('[') == EOF) { return EOF; } while (child) { if (this->printValue(child) == EOF) { return EOF; } child = child->next; if (child) { if (this->print(',') == EOF) { return EOF; } } } if (this->print(']') == EOF) { return EOF; } return 0; } int aJsonStream::parseObject(aJsonObject *item, char** filter) { int in = this->getch(); if (in != '{') { return EOF; } item->type = aJson_Object; this->skip(); in = this->getch(); if (in == '}') { return 0; } this->ungetch(in); aJsonObject* child; char first = -1; while ((first) || (in == ',')) { aJsonObject* new_item = aJsonClass::newItem(); if (new_item == NULL) { return EOF; } if (first) { first = 0; item->child = new_item; } else { child->next = new_item; new_item->prev = child; } child = new_item; this->skip(); if (this->parseString(child) == EOF) { return EOF; } this->skip(); child->name = child->valuestring; child->valuestring = NULL; in = this->getch(); if (in != ':') { return EOF; } this->skip(); if (this->parseValue(child, filter) == EOF) { return EOF; } this->skip(); in = this->getch(); } if (in == '}') { return 0; } else { return EOF; } } int aJsonStream::printObject(aJsonObject *item) { if (item == NULL) { //无操作 return 0; } aJsonObject *child = item->child; if (this->print('{') == EOF) { return EOF; } while (child) { if (this->printStringPtr(child->name) == EOF) { return EOF; } if (this->print(':') == EOF) { return EOF; } if (this->printValue(child) == EOF) { return EOF; } child = child->next; if (child) { if (this->print(',') == EOF) { return EOF; } } } if (this->print('}') == EOF) { return EOF; } return 0; } unsigned char aJsonClass::getArraySize(aJsonObject *array) { aJsonObject *c = array->child; unsigned char i = 0; while (c) i++, c = c->next; return i; } aJsonObject*aJsonClass::getArrayItem(aJsonObject *array, unsigned char item) { aJsonObject *c = array->child; while (c && item > 0) item--, c = c->next; return c; } aJsonObject*aJsonClass::getObjectItem(aJsonObject *object, const char *string) { aJsonObject *c = object->child; while (c && strcasecmp(c->name, string)) c = c->next; return c; } void aJsonClass::suffixObject(aJsonObject *prev, aJsonObject *item) { prev->next = item; item->prev = prev; } aJsonObject*aJsonClass::createReference(aJsonObject *item) { aJsonObject *ref = newItem(); if (!ref) return 0; memcpy(ref, item, sizeof(aJsonObject)); ref->name = 0; ref->type |= aJson_IsReference; ref->next = ref->prev = 0; return ref; } void aJsonClass::addItemToArray(aJsonObject *array, aJsonObject *item) { aJsonObject *c = array->child; if (!item) return; if (!c) { array->child = item; } else { while (c && c->next) c = c->next; suffixObject(c, item); } } void aJsonClass::addItemToObject(aJsonObject *object, const char *string, aJsonObject *item) { if (!item) return; if (item->name) free(item->name); item->name = strdup(string); addItemToArray(object, item); } void aJsonClass::addItemReferenceToArray(aJsonObject *array, aJsonObject *item) { addItemToArray(array, createReference(item)); } void aJsonClass::addItemReferenceToObject(aJsonObject *object, const char *string, aJsonObject *item) { addItemToObject(object, string, createReference(item)); } aJsonObject*aJsonClass::detachItemFromArray(aJsonObject *array, unsigned char which) { aJsonObject *c = array->child; while (c && which > 0) c = c->next, which--; if (!c) return 0; if (c->prev) c->prev->next = c->next; if (c->next) c->next->prev = c->prev; if (c == array->child) array->child = c->next; c->prev = c->next = 0; return c; } void aJsonClass::deleteItemFromArray(aJsonObject *array, unsigned char which) { deleteItem(detachItemFromArray(array, which)); } aJsonObject*aJsonClass::detachItemFromObject(aJsonObject *object, const char *string) { unsigned char i = 0; aJsonObject *c = object->child; while (c && strcasecmp(c->name, string)) i++, c = c->next; if (c) return detachItemFromArray(object, i); return 0; } void aJsonClass::deleteItemFromObject(aJsonObject *object, const char *string) { deleteItem(detachItemFromObject(object, string)); } void aJsonClass::replaceItemInArray(aJsonObject *array, unsigned char which, aJsonObject *newitem) { aJsonObject *c = array->child; while (c && which > 0) c = c->next, which--; if (!c) return; newitem->next = c->next; newitem->prev = c->prev; if (newitem->next) newitem->next->prev = newitem; if (c == array->child) array->child = newitem; else newitem->prev->next = newitem; c->next = c->prev = 0; deleteItem(c); } void aJsonClass::replaceItemInObject(aJsonObject *object, const char *string, aJsonObject *newitem) { unsigned char i = 0; aJsonObject *c = object->child; while (c && strcasecmp(c->name, string)) i++, c = c->next; if (c) { newitem->name = strdup(string); replaceItemInArray(object, i, newitem); } } aJsonObject*aJsonClass::createNull() { aJsonObject *item = newItem(); if (item) item->type = aJson_NULL; return item; } aJsonObject*aJsonClass::createItem(bool b) { aJsonObject *item = newItem(); if (item){ item->type = aJson_Boolean; item->valuebool = b; } return item; } aJsonObject*aJsonClass::createItem(char b) { aJsonObject *item = newItem(); if (item) { item->type = aJson_Boolean; item->valuebool = b ? -1 : 0; } return item; } aJsonObject*aJsonClass::createItem(int num) { aJsonObject *item = newItem(); if (item) { item->type = aJson_Int; item->valueint = (int) num; } return item; } aJsonObject*aJsonClass::createItem(double num) { aJsonObject *item = newItem(); if (item) { item->type = aJson_Float; item->valuefloat = num; } return item; } aJsonObject*aJsonClass::createItem(const char *string) { aJsonObject *item = newItem(); if (item) { item->type = aJson_String; item->valuestring = strdup(string); } return item; } aJsonObject*aJsonClass::createArray() { aJsonObject *item = newItem(); if (item) item->type = aJson_Array; return item; } aJsonObject*aJsonClass::createObject() { aJsonObject *item = newItem(); if (item) item->type = aJson_Object; return item; } aJsonObject*aJsonClass::createIntArray(int *numbers, unsigned char count) { unsigned char i; aJsonObject *n = 0, *p = 0, *a = createArray(); for (i = 0; a && i < count; i++) { n = createItem(numbers[i]); if (!i) a->child = n; else suffixObject(p, n); p = n; } return a; } aJsonObject*aJsonClass::createFloatArray(double *numbers, unsigned char count) { unsigned char i; aJsonObject *n = 0, *p = 0, *a = createArray(); for (i = 0; a && i < count; i++) { n = createItem(numbers[i]); if (!i) a->child = n; else suffixObject(p, n); p = n; } return a; } aJsonObject*aJsonClass::createDoubleArray(double *numbers, unsigned char count) { unsigned char i; aJsonObject *n = 0, *p = 0, *a = createArray(); for (i = 0; a && i < count; i++) { n = createItem(numbers[i]); if (!i) a->child = n; else suffixObject(p, n); p = n; } return a; } aJsonObject*aJsonClass::createStringArray(const char **strings, unsigned char count) { unsigned char i; aJsonObject *n = 0, *p = 0, *a = createArray(); for (i = 0; a && i < count; i++) { n = createItem(strings[i]); if (!i) a->child = n; else suffixObject(p, n); p = n; } return a; } void aJsonClass::addNullToObject(aJsonObject* object, const char* name) { addItemToObject(object, name, createNull()); } void aJsonClass::addBooleanToObject(aJsonObject* object, const char* name, bool b) { addItemToObject(object, name, createItem(b)); } void aJsonClass::addNumberToObject(aJsonObject* object, const char* name, int n) { addItemToObject(object, name, createItem(n)); } void aJsonClass::addNumberToObject(aJsonObject* object, const char* name, double n) { addItemToObject(object, name, createItem(n)); } void aJsonClass::addStringToObject(aJsonObject* object, const char* name, const char* s) { addItemToObject(object, name, createItem(s)); } aJsonClass aJson; 3.4产品展示 电路连接实物图如图311所示。 图311电路连接实物图 图311中手持的是WiFi模块ESP826601,使用其5个引脚(VCC、GND、CH_PD、RT、XT)。图片下方与水瓶相连的即两个水位传感器,用于检测工作层水位,进而控制开关,实现饮料机出水的功能。WiFi模块ESP826601和Arduino开发板相连接,可以实现装置与其他设备的通信。图片右端为上层直流电机(储水层和工作层之间的开关驱动电机),其功能和原理与下层直流电机相同。图片的右上方是触摸传感器,根据有无触摸信号来调整其输入Arduino开发板的电位高低。 图312和图313为实际演示图。纯手工自制的直流电机驱动开关由瓦楞纸板、亚克力板、齿轮组以及两个直流电机组成。当装置的工作状态切换时,开关也会相应地打开或关闭,同相转动的两个直流电机驱动齿轮带动连接的亚克力挡板转动,从而挡住或让出出水口,完成出水口的开关功能。 图312出水口打开 图313出水口关闭 3.5故障及问题分析 问题1: 水位传感器工作稳定性比较差。 解决方案: 通过反复试验,利用串口监视器实时监测两个传感器的水位信息,最后选择合适的水位标准作为工作状态切换的水位值。由于水位下降后,依然有水珠附着在水位传感器表面,导致水位传感器测量下降水位时不如测量水位上升时灵敏,因此设置水位时上下水位差导致的容量差应比实际小。 问题2: 直流电机的正转反转。 解决方案: 因为直流电机的作用是驱动分隔储水层、工作层和接水层的两个开关,开关既能打开也能关闭,因此直流电机也要能做到正转和反转。方法是直流电机原来接在GND的线接在数字I/O引脚,这样正负极引脚的相对电压便可决定直流电机的正转与反转(正为HIGH且负为LOW则正转; 正为LOW且负为HIGH则反转)。 问题3: 直流电机的速度调节。 解决方案: 直流电机速度过快会导致开关损坏,因此希望能调节直流电机的速度。实验中发现一个普通直流电机无法实现,最后采用两个直流电机组合的方式驱动开关。使用Arduino开发板数字I/O的PWM引脚,analogWrite(engine,A)函数,A为0~255的整数则可以调节输出电压的大小,进而改变直流电机的运行速度。 问题4: 工艺实现部分如何防水的问题。 解决方案: 在工艺实现时避免不了和水打交道,瓦楞纸板和电路都不能直接接触水,因此项目中用热熔胶作为电路粘合剂,既操作方便,又可以起到隔水的作用。 问题5: 代码编译通过并烧写后出现问题。 解决方案: 实验过程中出现最多的是水位传感器数据实时更新的问题。原来打算使用自带的loop循环对水位传感器的数据进行实时更新,但后来发现算法过于复杂。最后,使用for循环对水位传感器的输入进行循环数据读取,并设置条件使其在合适的水位跳出循环改变工作状态。 实验过程中,使用串口通信并在代码中添加注释是更快解决算法问题和调试程序的重要途径。通过串口监视器,可以根据Arduino开发板的反馈信息判断当前的工作状态并找到错误所在。 问题6: WiFi模块在计算机上进行串口调试时出现一些问题。 解决方案: 由于项目中采取的是基础网络拓扑结构,基于Station模式,将模块当作一个设备(Client)连接到局域网内的路由器,故应当将WiFi模块和发送信息端(手机、计算机)连接在同一个无线接入点,通过AP的转发来完成通信。另外,应熟练掌握AT指令的用法,尤其是透传模式的进入与退出操作,退出时需要输入“+++”并取消发送新行,避免使之后的AT指令无效。同时,应注意串口的波特率设置(一般为9600或115200),否则输出将出现乱码。 问题7: WiFi模块连接Arduino开发板时出现的一些问题。 解决方案: 由于WiFi模块是通过RX(接收)和TX(发送)这两个引脚进行串口通信的,占用了Arduino IDE上传程序的串口,所以在烧写程序时应当将WiFi模块与RX和TX口的连线断开,防止上传失败。另外,ESP8266必须使用3.3V电压源进行供电,并且在数字输出引脚选择阻值合适的上拉电阻,避免由于电平紊乱导致通信发生错误。 3.6元件清单 完成本项目所用到的元件及数量如表34所示。 表34自助饮料机元件清单 模块元件/测试仪表数量 实体执行模块 导线若干 杜邦线若干 水位传感器2个 触摸传感器5个 300直流电机 3个 LED发光二极管3个 电阻3个 供电电源1个 亚克力板1个 瓦楞纸板若干 2mm齿轮组(2mm齿轮、2mm转动杆轴以及连接杆)若干 WiFi模块 导线若干 杜邦线若干 ESP8266011个 LED发光二极管1个 电阻10kΩ1个