├── .gitignore ├── ArduinoFirmwareEsp ├── ArduinoFirmwareEsp.ino ├── ArduinoMcuOTA.ino ├── CommCmd.h ├── CommItf.cpp ├── CommItf.h ├── CommLgc.cpp ├── CommLgc.h ├── Configuration.cpp ├── Configuration.h ├── WebServer.ino ├── config.h ├── data │ ├── img │ │ ├── favicon.ico │ │ ├── icons.png │ │ ├── logo.ico │ │ ├── logo.png │ │ ├── logoOtto.ico │ │ ├── logoOtto.png │ │ ├── logoPrimo.ico │ │ ├── logoPrimo.png │ │ ├── logoUnoWiFi.ico │ │ └── logoUnoWiFi.png │ ├── index.html │ ├── pure.css │ ├── style.css │ ├── ui.js │ ├── wifi.html │ └── wifi.js └── utility │ └── wifi_utils.h ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | *.zip 32 | .DS_Store 33 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/ArduinoFirmwareEsp.ino: -------------------------------------------------------------------------------- 1 | #include "CommLgc.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "Configuration.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int ledState = LOW; // used to set the LED state 14 | long previousMillis = 0; // will store last time LED was updated 15 | long ap_interval = 50; //blink interval in ap mode 16 | IPAddress default_IP(192,168,240,1); //defaul IP Address 17 | String HOSTNAME = DEF_HOSTNAME; 18 | String staticIP_param ; 19 | String netmask_param; 20 | String gateway_param; 21 | String dhcp = "on"; 22 | 23 | ESP8266WebServer server(80); //server UI 24 | 25 | void setup() { 26 | 27 | #if defined(MCU_OTA) 28 | _setup_dfu(); 29 | #endif 30 | 31 | pinMode(WIFI_LED, OUTPUT); //initialize wifi LED 32 | digitalWrite(WIFI_LED, LOW); 33 | ArduinoOTA.begin(); //OTA ESP 34 | initMDNS(); 35 | CommunicationLogic.begin(); 36 | SPIFFS.begin(); 37 | initHostname(); 38 | setWiFiConfig(); 39 | initWebServer(); //UI begin 40 | 41 | } 42 | 43 | void loop() { 44 | 45 | ArduinoOTA.handle(); 46 | CommunicationLogic.handle(); 47 | handleWebServer(); 48 | wifiLed(); 49 | 50 | #if defined(MCU_OTA) 51 | _handle_Mcu_OTA(); 52 | #endif 53 | 54 | 55 | } 56 | 57 | void initMDNS(){ 58 | 59 | MDNS.begin(HOSTNAME.c_str()); 60 | MDNS.setInstanceName(HOSTNAME); 61 | MDNS.addServiceTxt("arduino", "tcp", "fw_name", FW_NAME); 62 | MDNS.addServiceTxt("arduino", "tcp", "fw_version", FW_VERSION); 63 | 64 | } 65 | 66 | void initHostname(){ 67 | //retrieve user defined hostname 68 | String tmpHostname = Config.getParam("hostname"); 69 | if( tmpHostname!="" ) 70 | HOSTNAME = tmpHostname; 71 | WiFi.hostname(HOSTNAME); 72 | 73 | } 74 | 75 | void wifiLed(){ 76 | 77 | unsigned long currentMillis = millis(); 78 | int wifi_status = WiFi.status(); 79 | if ((WiFi.getMode() == 1 || WiFi.getMode() == 3) && wifi_status == WL_CONNECTED) { //wifi LED in STA MODE 80 | if (currentMillis - previousMillis > ap_interval) { 81 | previousMillis = currentMillis; 82 | if (ledState == LOW){ 83 | ledState = HIGH; 84 | ap_interval = 200; //time wifi led ON 85 | } 86 | else{ 87 | ledState = LOW; 88 | ap_interval = 2800; //time wifi led OFF 89 | } 90 | digitalWrite(WIFI_LED, ledState); 91 | } 92 | } 93 | else{ //if (WiFi.softAPgetStationNum() > 0 ) { //wifi LED on in AP mode 94 | if (currentMillis - previousMillis > ap_interval) { 95 | previousMillis = currentMillis; 96 | if (ledState == LOW){ 97 | ledState = HIGH; 98 | ap_interval = 950; 99 | } 100 | else{ 101 | ledState = LOW; 102 | ap_interval = 50; 103 | } 104 | digitalWrite(WIFI_LED, ledState); 105 | } 106 | } 107 | 108 | } 109 | 110 | void setWiFiConfig(){ 111 | 112 | //WiFi mode is remembered by the esp sdk 113 | if (WiFi.getMode() != WIFI_STA) { 114 | 115 | //set default AP 116 | String mac = WiFi.macAddress(); 117 | String apSSID = String(SSIDNAME) + "-" + String(mac[9])+String(mac[10])+String(mac[12])+String(mac[13])+String(mac[15])+String(mac[16]); 118 | WiFi.softAP(apSSID.c_str()); 119 | WiFi.softAPConfig(default_IP, default_IP, IPAddress(255, 255, 255, 0)); //set default ip for AP mode 120 | } 121 | //set STA mode 122 | #if defined(ESP_CH_SPI) 123 | ETS_SPI_INTR_DISABLE(); 124 | #endif 125 | { // first static config if configured 126 | String staticIP = Config.getParam("staticIP").c_str(); 127 | if (staticIP != "" && staticIP != "0.0.0.0") { 128 | dhcp = "off"; 129 | staticIP_param = staticIP; 130 | netmask_param = Config.getParam("netMask").c_str(); 131 | gateway_param = Config.getParam("gatewayIP").c_str(); 132 | WiFi.config(stringToIP(staticIP_param), stringToIP(gateway_param), stringToIP(netmask_param)); 133 | } 134 | } 135 | 136 | WiFi.begin(); // connect to AP with credentials remembered by esp sdk 137 | if (WiFi.waitForConnectResult() != WL_CONNECTED && WiFi.getMode() == WIFI_STA) { 138 | // if STA didn't connect, start AP 139 | WiFi.mode(WIFI_AP_STA); // STA must be active for library connects 140 | setWiFiConfig(); // setup AP 141 | } 142 | #if defined(ESP_CH_SPI) 143 | ETS_SPI_INTR_ENABLE(); 144 | #endif 145 | } 146 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/ArduinoMcuOTA.ino: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #if defined(MCU_OTA) 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #if defined(STAROTTO) 13 | #include 14 | #elif defined(UNOWIFIDEVED) 15 | #include 16 | #include 17 | #elif defined (GENERIC_ESP8266) 18 | #ifdef ESP_CH_SPI 19 | #include 20 | #include 21 | #else 22 | #include 23 | #include 24 | #endif 25 | #endif 26 | 27 | struct dfu_data *global_dfu; 28 | struct dfu_binary_file *global_binary_file; 29 | 30 | static int serial_release(void *dummy) 31 | { 32 | //stop Serial communication 33 | #ifdef ESP_CH_SPI 34 | SPISlave.end(); 35 | #else 36 | Serial.end(); 37 | #endif 38 | return 0; 39 | } 40 | 41 | static int _setup_dfu(void) 42 | { 43 | #if defined(STAROTTO) 44 | global_dfu = dfu_init(&esp8266_serial_star8_interface_ops, 45 | NULL, 46 | NULL, 47 | serial_release, 48 | NULL, 49 | &stm32_dfu_target_ops, 50 | &stm32f469bi_device_data, 51 | &esp8266_dfu_host_ops); 52 | #elif defined(UNOWIFIDEVED) 53 | global_dfu = dfu_init(&esp8266_serial_arduino_unowifi_interface_ops, 54 | NULL, 55 | NULL, 56 | serial_release, 57 | NULL, 58 | &stk500_dfu_target_ops, 59 | &atmega328p_device_data, 60 | &esp8266_dfu_host_ops); 61 | #elif defined(GENERIC_ESP8266) 62 | #ifdef ESP_CH_SPI 63 | global_dfu = dfu_init(&esp8266_spi_arduinouno_hacked_interface_ops, 64 | NULL, 65 | NULL, 66 | serial_release, 67 | NULL, 68 | &avrisp_dfu_target_ops, 69 | &atmega328p_device_data, 70 | &esp8266_dfu_host_ops); 71 | #else 72 | global_dfu = dfu_init(&esp8266_serial_arduinouno_hacked_interface_ops, 73 | NULL, 74 | NULL, 75 | serial_release, 76 | NULL, 77 | &stk500_dfu_target_ops, 78 | &atmega328p_device_data, 79 | &esp8266_dfu_host_ops); 80 | #endif 81 | #endif 82 | 83 | if (!global_dfu) { 84 | /* FIXME: Is this ok ? */ 85 | return -1; 86 | } 87 | 88 | global_binary_file = dfu_binary_file_start_rx(&dfu_rx_method_http_arduino, global_dfu, &server); 89 | if (!global_binary_file) { 90 | return -1; 91 | } 92 | 93 | if (dfu_binary_file_flush_start(global_binary_file) < 0) { 94 | return -1; 95 | } 96 | return 0; 97 | } 98 | 99 | void _finalize_dfu(void) 100 | { 101 | dfu_binary_file_fini(global_binary_file); 102 | dfu_fini(global_dfu); 103 | global_dfu = NULL; 104 | global_binary_file = NULL; 105 | } 106 | 107 | void _handle_Mcu_OTA(void) 108 | { 109 | if (!global_dfu) 110 | _setup_dfu(); 111 | if (!global_dfu) 112 | return; 113 | switch (dfu_idle(global_dfu)) { 114 | case DFU_ERROR: 115 | _finalize_dfu(); 116 | break; 117 | case DFU_ALL_DONE: 118 | dfu_target_go(global_dfu); 119 | _finalize_dfu(); 120 | delay(80); 121 | ESP.reset(); 122 | break; 123 | case DFU_CONTINUE: 124 | break; 125 | } 126 | } 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/CommCmd.h: -------------------------------------------------------------------------------- 1 | enum { 2 | RESET_ESP_CMD = 0x01, 3 | 4 | SET_NET_CMD = 0x10, 5 | SET_PASSPHRASE_CMD = 0x11, 6 | SET_KEY_CMD = 0x12, 7 | TEST_CMD = 0x13, 8 | SET_IP_CONFIG_CMD = 0x14, 9 | SET_DNS_CONFIG_CMD = 0x15, 10 | 11 | GET_CONN_STATUS_CMD = 0x20, 12 | GET_IPADDR_CMD = 0x21, 13 | GET_MACADDR_CMD = 0x22, 14 | GET_CURR_SSID_CMD = 0x23, 15 | GET_CURR_BSSID_CMD = 0x24, 16 | GET_CURR_RSSI_CMD = 0x25, 17 | GET_CURR_ENCT_CMD = 0x26, 18 | SCAN_NETWORKS = 0x27, 19 | START_SERVER_TCP_CMD= 0x28, 20 | GET_STATE_TCP_CMD = 0x29, 21 | DATA_SENT_TCP_CMD = 0x2A, 22 | AVAIL_DATA_TCP_CMD = 0x2B, 23 | GET_DATA_TCP_CMD = 0x2C, 24 | START_CLIENT_TCP_CMD= 0x2D, 25 | STOP_CLIENT_TCP_CMD = 0x2E, 26 | GET_CLIENT_STATE_TCP_CMD= 0x2F, 27 | DISCONNECT_CMD = 0x30, 28 | GET_IDX_SSID_CMD = 0x31, 29 | GET_IDX_RSSI_CMD = 0x32, 30 | GET_IDX_ENCT_CMD = 0x33, 31 | REQ_HOST_BY_NAME_CMD= 0x34, 32 | GET_HOST_BY_NAME_CMD= 0x35, 33 | START_SCAN_NETWORKS = 0x36, 34 | GET_FW_VERSION_CMD = 0x37, 35 | GET_TEST_CMD = 0x38, 36 | SEND_DATA_UDP_CMD = 0x39, 37 | GET_REMOTE_DATA_CMD = 0x3A, 38 | 39 | // All command with DATA_FLAG 0x40 send a 16bit Len 40 | 41 | SEND_DATA_TCP_CMD = 0x44, 42 | GET_DATABUF_TCP_CMD = 0x45, 43 | INSERT_DATABUF_CMD = 0x46, 44 | }; 45 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/CommItf.cpp: -------------------------------------------------------------------------------- 1 | //Communication Interface 2 | #include "CommItf.h" 3 | #include "utility/wifi_utils.h" 4 | #include "SPISlave.h" 5 | 6 | uint8_t raw_pckt[SPI_BUFFER_SIZE]; //SPI buffer (limited to 128 bytes) 7 | //String raw_pckt_serial; 8 | uint8_t data_received_size = 0; //check received data (maximum 128 byte (4 * 32)) 9 | bool req_send = false; //check data sent for lenght greater than 32 byte (SPI) 10 | bool processing= false; //check data ready to process (SPI) 11 | CommItf* This; //need for SPI event 12 | 13 | unsigned long _startMillis; //need to Serial 14 | unsigned long _timeout = 1000; //1 Second Serial Timeout for Serial 15 | 16 | CommItf::CommItf(){ 17 | } 18 | 19 | bool CommItf::begin(){ 20 | 21 | #if defined ESP_CH_UART 22 | Serial.begin(BAUDRATE_COMMUNICATION); 23 | while(!Serial); 24 | #ifdef DEBUG 25 | Serial1.begin(BAUDRATE_DEBUG); 26 | Serial1.println("--SERIAL started--"); 27 | #endif 28 | return true; 29 | 30 | #elif defined ESP_CH_SPI 31 | pinMode(SLAVE_READY_PIN,OUTPUT); //set SlaveReady pin 32 | digitalWrite(SLAVE_READY_PIN,LOW); //set slaveready to HIGH level when ESP is ready to send data 33 | SPISlaveInit(); 34 | #ifdef DEBUG 35 | Serial.begin(BAUDRATE_DEBUG); 36 | Serial.println("--SPI started--"); 37 | #endif 38 | return true; 39 | #endif 40 | 41 | return false; 42 | } 43 | 44 | bool CommItf::available(){ 45 | #if defined ESP_CH_UART 46 | return Serial.available(); 47 | #elif defined ESP_CH_SPI 48 | return processing; 49 | #endif 50 | } 51 | 52 | int CommItf::read(tMsgPacket *_pckt){ 53 | 54 | return createPacket(_pckt); 55 | } 56 | 57 | /* Cmd Struct Message */ 58 | /* _________________________________________________________________________________ */ 59 | /*| START CMD | C/R | CMD |[TOT LEN]| N.PARAM | PARAM LEN | PARAM | .. | END CMD | */ 60 | /*|___________|______|______|_________|_________|___________|________|____|_________| */ 61 | /*| 8 bit | 1bit | 7bit | 8bit | 8bit | 8bit | nbytes | .. | 8bit | */ 62 | /*|___________|______|______|_________|_________|___________|________|____|_________| */ 63 | 64 | int CommItf::createPacket(tMsgPacket *_reqPckt){ 65 | 66 | #if defined ESP_CH_UART 67 | Serial.readBytesUntil(END_CMD, raw_pckt, SPI_BUFFER_SIZE); 68 | #endif 69 | 70 | int idx = 0; 71 | unsigned char tmp; 72 | if(raw_pckt[idx] != START_CMD){ //TODO 73 | return -1; 74 | } 75 | _reqPckt->cmd = raw_pckt[idx]; 76 | //The command 77 | _reqPckt->tcmd = raw_pckt[++idx]; 78 | 79 | //The number of parameters for the command 80 | tmp = raw_pckt[++idx]; 81 | _reqPckt->nParam = tmp; 82 | //Get each parameter 83 | for(int a=0; a<(int)_reqPckt->nParam; a++){ 84 | //Length of the parameter 85 | if( _reqPckt->tcmd >= 0x40 && _reqPckt->tcmd < 0x50 ){ 86 | //16bit tParam 87 | tmp = (uint16_t)((raw_pckt[++idx] << 8) + (uint8_t)raw_pckt[++idx]); 88 | _reqPckt->paramsData[a].dataLen = tmp; 89 | memcpy(_reqPckt->paramsData[a].data,raw_pckt+(++idx),tmp); 90 | idx = idx+(tmp-1); 91 | } 92 | else{ 93 | //8bit tParamData 94 | tmp = raw_pckt[++idx]; 95 | _reqPckt->params[a].paramLen = tmp; 96 | memcpy(_reqPckt->params[a].param,raw_pckt+(++idx),tmp); 97 | idx = idx+(tmp-1); 98 | } 99 | } 100 | return 0; 101 | 102 | } 103 | 104 | void CommItf::write(uint8_t* _pckt,int transfer_size){ 105 | #if defined ESP_CH_UART 106 | Serial.write(_pckt,transfer_size); 107 | #elif defined ESP_CH_SPI 108 | SPISlaveWrite(_pckt,transfer_size); 109 | #endif 110 | } 111 | 112 | // void CommItf::end(){ 113 | // if(CommChannel == CH_SERIAL) 114 | // Serial.end(); 115 | // } 116 | 117 | /** Private Functions **/ 118 | 119 | #if defined ESP_CH_SPI 120 | void CommItf::SPISlaveInit(){ 121 | This = this; 122 | //SPI Data register 123 | SPISlave.onData([](uint8_t * data, size_t len) { 124 | if(data_received_size<4){ 125 | digitalWrite(SLAVE_READY_PIN,LOW); 126 | memcpy(raw_pckt+(data_received_size*32),data, len); 127 | data_received_size++; 128 | } 129 | }); 130 | 131 | //SPI Status register 132 | SPISlave.onStatus([](uint32_t data) { 133 | if(data==SPI_DATA_READY){ 134 | if(raw_pckt[0]==START_CMD){ 135 | processing = true; 136 | } 137 | } 138 | else if(data==SPI_DATA_RECEIVED){ 139 | digitalWrite(SLAVE_READY_PIN,LOW); 140 | req_send = true; 141 | } 142 | else 143 | Serial.println("status error"); 144 | }); 145 | 146 | // Setup SPI Slave registers and pins 147 | SPISlave.begin(); 148 | 149 | } 150 | 151 | void CommItf::SPISlaveWrite(uint8_t* _resPckt,int transfer_size){ 152 | SPISlave.setData((uint8_t *)_resPckt,32); //send response to MCU 153 | digitalWrite(SLAVE_READY_PIN,HIGH); 154 | transfer_size = ceil((float)transfer_size/32); 155 | if(transfer_size > 0){ //response length greater than 32 bytes 156 | for(int i=1;i 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | DEALINGS IN THE SOFTWARE 21 | 22 | */ 23 | 24 | #include "Arduino.h" 25 | #include "utility/wifi_utils.h" 26 | #include "config.h" 27 | 28 | #ifndef H_COMM_ITF_H 29 | #define H_COMM_ITF_H 30 | 31 | // enum CHANNEL { 32 | // CH_SERIAL = 0, 33 | // CH_SPI 34 | // }; 35 | // 36 | // enum MCU { 37 | // AVR328P = 0, 38 | // NRF52, 39 | // STM32, 40 | // SAMD21 41 | // }; 42 | 43 | class CommItf { 44 | 45 | public: 46 | 47 | CommItf(); 48 | bool begin(); 49 | int read(tMsgPacket *_pck); 50 | void write(uint8_t *_pck, int transfer_size); 51 | void end(); 52 | bool available(); 53 | 54 | private: 55 | 56 | int createPacket(tMsgPacket *_pck); 57 | 58 | /*SPI*/ 59 | #if defined ESP_CH_SPI 60 | void SPISlaveInit(); 61 | void SPISlaveWrite(uint8_t* _resPckt,int transfer_size); 62 | #endif 63 | 64 | /*Serial*/ 65 | #if defined ESP_CH_UART 66 | String readStringUntil(char); 67 | int timedRead(); 68 | #endif 69 | 70 | }; 71 | 72 | extern CommItf CommunicationInterface; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/CommLgc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright <2017> 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | DEALINGS IN THE SOFTWARE 21 | 22 | */ 23 | 24 | //Communication Interface 25 | #include "CommLgc.h" 26 | #include "CommCmd.h" 27 | 28 | //cached values 29 | IPAddress _reqHostIp; 30 | 31 | IPAddress* _handyIp; 32 | IPAddress* _handySubnet; 33 | IPAddress* _handyGateway; 34 | 35 | uint8_t tcpResult = 0; //used by availData function 36 | uint16_t bufferSize = 0; //used by getDataBuf function 37 | uint8_t numNets; //number of networks scanned 38 | 39 | //WiFiServer, WiFiClient and WiFiUDP map 40 | WiFiServer* mapWiFiServers[MAX_SOCK_NUMBER]; 41 | WiFiClient mapWiFiClients[MAX_SOCK_NUMBER]; 42 | WiFiUDP mapWiFiUDP[MAX_SOCK_NUMBER]; 43 | 44 | tMsgPacket _reqPckt; //initialize struct to receive a command from MCU 45 | uint8_t _resPckt[RESPONSE_LENGHT]; //response array 46 | int transfer_size = 0; //size of array response (length/32) 47 | 48 | CommLgc::CommLgc(){ 49 | } 50 | 51 | /** Logic Functions **/ 52 | 53 | void CommLgc::begin(){ 54 | while(!CommunicationInterface.begin()); 55 | } 56 | 57 | void CommLgc::handle(){ 58 | if(CommunicationInterface.available()){ 59 | if(CommunicationInterface.read(&_reqPckt)==0){ 60 | process(); 61 | CommunicationInterface.write(_resPckt,transfer_size); 62 | transfer_size = 0; //reset transfer size 63 | if (_reqPckt.tcmd == RESET_ESP_CMD) { 64 | ESP.reset(); 65 | } 66 | } 67 | else{ 68 | //TODO 69 | } 70 | } 71 | } 72 | 73 | void CommLgc::createErrorResponse(){ 74 | _resPckt[0] = ERR_CMD; 75 | _resPckt[1] = 0; 76 | _resPckt[2] = END_CMD; 77 | } 78 | 79 | void CommLgc::process(){ 80 | 81 | if ((_reqPckt.cmd == START_CMD) && 82 | ((_reqPckt.tcmd & REPLY_FLAG) == 0)){ 83 | 84 | _resPckt[0] = 0xE0; 85 | _resPckt[1] = _reqPckt.tcmd | REPLY_FLAG; 86 | 87 | switch(_reqPckt.tcmd){ 88 | case RESET_ESP_CMD: resetESP(); break; 89 | case SET_NET_CMD: begin(0); break; 90 | case SET_PASSPHRASE_CMD: begin(1); break; 91 | case SET_IP_CONFIG_CMD: config(); break; 92 | case SET_DNS_CONFIG_CMD: setDNS(); break; 93 | case GET_CONN_STATUS_CMD: getStatus(); break; 94 | case GET_IPADDR_CMD: getNetworkData(); break; 95 | case GET_MACADDR_CMD: getMacAddress(); break; 96 | case GET_CURR_SSID_CMD: getCurrentSSID(); break; 97 | case GET_CURR_BSSID_CMD: getBSSID(1); break; 98 | case GET_CURR_RSSI_CMD: getRSSI(1); break; 99 | case GET_CURR_ENCT_CMD: getEncryption(1); break; 100 | case SCAN_NETWORKS: scanNetwork(); break; 101 | case START_SERVER_TCP_CMD: startServer(); break; 102 | case GET_STATE_TCP_CMD: serverStatus(); break; 103 | case DATA_SENT_TCP_CMD: checkDataSent(); break; 104 | case AVAIL_DATA_TCP_CMD: availData(); break; 105 | case GET_DATA_TCP_CMD: getData(); break; 106 | case START_CLIENT_TCP_CMD: startClient(); break; 107 | case STOP_CLIENT_TCP_CMD: stopClient(); break; 108 | case GET_CLIENT_STATE_TCP_CMD: clientStatus(); break; 109 | case DISCONNECT_CMD: disconnect(); break; 110 | case GET_IDX_RSSI_CMD: getRSSI(0); break; 111 | case GET_IDX_ENCT_CMD: getEncryption(0); break; 112 | case REQ_HOST_BY_NAME_CMD: reqHostByName(); break; 113 | case GET_HOST_BY_NAME_CMD: getHostByName(); break; 114 | case GET_FW_VERSION_CMD: getFwVersion(); break; 115 | case START_SCAN_NETWORKS: startScanNetwork(); break; 116 | case SEND_DATA_UDP_CMD: sendUdpData(); break; 117 | case GET_REMOTE_DATA_CMD: remoteData(); break; 118 | case SEND_DATA_TCP_CMD: sendData(); break; 119 | case GET_DATABUF_TCP_CMD: getDataBuf(); break; 120 | case INSERT_DATABUF_CMD: insDataBuf(); break; 121 | default: createErrorResponse(); break; 122 | } 123 | } 124 | } 125 | 126 | /* Commands Functions */ 127 | 128 | void CommLgc::resetESP() { 129 | int resp_idx = 2; 130 | bool result = true; 131 | // no action - reset is in handle() after sending the response 132 | //set the response struct 133 | _resPckt[resp_idx++] = PARAM_NUMS_1; 134 | _resPckt[resp_idx++] = PARAM_SIZE_1; 135 | _resPckt[resp_idx++] = result; 136 | _resPckt[resp_idx++] = END_CMD; 137 | transfer_size = resp_idx; 138 | } 139 | 140 | /* WiFi Base */ 141 | void CommLgc::getRSSI(uint8_t current){ 142 | 143 | int resp_idx=2; 144 | int32_t result; 145 | //retrieve RSSI 146 | if(current == 1){ 147 | result = WiFi.RSSI(); 148 | } 149 | else{ 150 | uint8_t net_idx = _reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 151 | 152 | // NOTE: only for test this function 153 | // user must call scan network before 154 | // int num = WiFi.scanNetworks(); 155 | result = WiFi.RSSI(net_idx); 156 | } 157 | 158 | //Response contains 1 param with length 4 159 | _resPckt[resp_idx++] = PARAM_NUMS_1; 160 | _resPckt[resp_idx++] = PARAM_SIZE_4; 161 | _resPckt[resp_idx++] = result; 162 | _resPckt[resp_idx++] = 0xFF; 163 | _resPckt[resp_idx++] = 0xFF; 164 | _resPckt[resp_idx++] = 0xFF; 165 | _resPckt[resp_idx++] = END_CMD; 166 | transfer_size = resp_idx; 167 | 168 | } 169 | 170 | void CommLgc::getCurrentSSID(){ 171 | 172 | int resp_idx = 2; 173 | 174 | //retrieve SSID of the current network 175 | String result = WiFi.SSID(); 176 | 177 | //set the response struct 178 | _resPckt[resp_idx++] = 1; 179 | _resPckt[resp_idx++] = result.length(); 180 | memcpy(_resPckt+resp_idx,result.c_str(),result.length()); 181 | resp_idx = resp_idx+result.length(); 182 | _resPckt[resp_idx++] = END_CMD; 183 | transfer_size = resp_idx; 184 | 185 | } 186 | 187 | void CommLgc::getEncryption(uint8_t current){ 188 | 189 | uint8_t result = 0; 190 | uint8_t net_idx = 0; //network index 191 | int resp_idx = 2; 192 | 193 | if(current == 1){ 194 | //uint8_t numNets = WiFi.scanNetworks(); //get networks numbers 195 | String currSSID = WiFi.SSID(); //get current SSID 196 | for(int i=0; i=0; i++, j--){ 229 | _resPckt[resp_idx++]=mac[j]; 230 | } 231 | _resPckt[resp_idx++] = END_CMD; 232 | transfer_size = resp_idx; 233 | 234 | } 235 | 236 | void CommLgc::disconnect(){ 237 | 238 | int resp_idx = 2; 239 | bool result; 240 | 241 | //Disconnet from the network 242 | result = WiFi.disconnect(); 243 | 244 | //set the response struct 245 | _resPckt[resp_idx++] = PARAM_NUMS_1; 246 | _resPckt[resp_idx++] = PARAM_SIZE_1; 247 | _resPckt[resp_idx++] = result; 248 | _resPckt[resp_idx++] = END_CMD; 249 | transfer_size = resp_idx; 250 | 251 | } 252 | 253 | void CommLgc::getStatus(){ 254 | 255 | int resp_idx = 2; 256 | uint8_t result; 257 | 258 | //Disconnet from the network 259 | result = WiFi.status(); 260 | 261 | //set the response struct 262 | _resPckt[resp_idx++] = PARAM_NUMS_1; 263 | _resPckt[resp_idx++] = PARAM_SIZE_1; 264 | _resPckt[resp_idx++] = result; 265 | _resPckt[resp_idx++] = END_CMD; 266 | transfer_size = resp_idx; 267 | 268 | } 269 | 270 | void CommLgc::begin(uint8_t idx){ 271 | 272 | int resp_idx = 2; 273 | uint8_t result; 274 | 275 | if(idx == 0){ // if idx==0 connection without password 276 | //retrieve parameters 277 | char ssid[_reqPckt.params[PARAM_NUMS_0].paramLen+1]; 278 | //strncpy(ssid, _reqPckt.params[0].param, _reqPckt.params[0].paramLen); 279 | memcpy(ssid, _reqPckt.params[PARAM_NUMS_0].param, _reqPckt.params[PARAM_NUMS_0].paramLen); 280 | ssid[_reqPckt.params[PARAM_NUMS_0].paramLen] = '\0'; 281 | //set network and retrieve result 282 | result = WiFi.begin(ssid); 283 | } 284 | else{ 285 | //retrieve parameters 286 | char ssid[_reqPckt.params[PARAM_NUMS_0].paramLen+1]; 287 | char pass[_reqPckt.params[PARAM_NUMS_1].paramLen+1]; 288 | //strncpy(ssid, _reqPckt.params[0].param, _reqPckt.params[0].paramLen); 289 | //strncpy(pass, _reqPckt.params[1].param, _reqPckt.params[1].paramLen); 290 | memcpy(ssid, _reqPckt.params[PARAM_NUMS_0].param, _reqPckt.params[PARAM_NUMS_0].paramLen); 291 | memcpy(pass, _reqPckt.params[PARAM_NUMS_1].param, _reqPckt.params[PARAM_NUMS_1].paramLen); 292 | ssid[_reqPckt.params[PARAM_NUMS_0].paramLen] = '\0'; 293 | pass[_reqPckt.params[PARAM_NUMS_1].paramLen] = '\0'; 294 | //set network and retrieve result 295 | result = WiFi.begin(ssid, pass); 296 | } 297 | 298 | //set the response struct 299 | _resPckt[resp_idx++] = PARAM_NUMS_1; 300 | _resPckt[resp_idx++] = PARAM_SIZE_1; 301 | _resPckt[resp_idx++] = result; 302 | _resPckt[resp_idx++] = END_CMD; 303 | transfer_size = resp_idx; 304 | 305 | } 306 | 307 | void CommLgc::startScanNetwork(){ 308 | 309 | int resp_idx = 2; 310 | //set the response struct 311 | _resPckt[resp_idx++] = PARAM_NUMS_1; 312 | _resPckt[resp_idx++] = PARAM_SIZE_1; 313 | _resPckt[resp_idx++] = 1; 314 | _resPckt[resp_idx++] = END_CMD; 315 | transfer_size = resp_idx; 316 | 317 | } 318 | 319 | void CommLgc::scanNetwork(){ 320 | 321 | int resp_idx = 2; 322 | //scanNetworks command 323 | numNets = WiFi.scanNetworks(); 324 | //fix the maximum network number to 10 325 | uint8_t numNetsMax = 10; 326 | 327 | numNets = (numNets <= numNetsMax) ? numNets : numNetsMax; 328 | _resPckt[resp_idx++]=numNets; 329 | for (int i=0; i=0; j--){ 371 | _resPckt[resp_idx++] = result[j]; 372 | } 373 | _resPckt[resp_idx++] = END_CMD; 374 | transfer_size = resp_idx; 375 | 376 | } 377 | 378 | void CommLgc::config(){ 379 | /* 380 | WiFi.config call arduino side: WiFi.config(local_ip, gateway, subnet, dns1, dns2) 381 | */ 382 | 383 | //TODO: To be tested 384 | bool result; 385 | int resp_idx = 2; 386 | uint8_t validParams = 0; 387 | 388 | uint8_t stip0, stip1, stip2, stip3, 389 | gwip0, gwip1, gwip2, gwip3, 390 | snip0, snip1, snip2, snip3; 391 | 392 | validParams = _reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 393 | 394 | //retrieve the static IP address 395 | stip0 = _reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_0]; 396 | stip1 = _reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_1]; 397 | stip2 = _reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_2]; 398 | stip3 = _reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_3]; 399 | _handyIp = new IPAddress(stip0, stip1, stip2, stip3); 400 | 401 | 402 | //retrieve the gateway IP address 403 | gwip0 = _reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_0]; 404 | gwip1 = _reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_1]; 405 | gwip2 = _reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_2]; 406 | gwip3 = _reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_3]; 407 | _handyGateway = new IPAddress(gwip0, gwip1, gwip2, gwip3); 408 | 409 | //retrieve the subnet mask 410 | snip0 = _reqPckt.params[PARAM_NUMS_3].param[PARAM_NUMS_0]; 411 | snip1 = _reqPckt.params[PARAM_NUMS_3].param[PARAM_NUMS_1]; 412 | snip2 = _reqPckt.params[PARAM_NUMS_3].param[PARAM_NUMS_2]; 413 | snip3 = _reqPckt.params[PARAM_NUMS_3].param[PARAM_NUMS_3]; 414 | _handySubnet = new IPAddress(snip0, snip1, snip2, snip3); 415 | 416 | result = WiFi.config(*_handyIp, *_handyGateway, *_handySubnet); 417 | 418 | _resPckt[resp_idx++] = PARAM_NUMS_1; 419 | _resPckt[resp_idx++] = PARAM_SIZE_1; 420 | _resPckt[resp_idx++] = result; 421 | _resPckt[resp_idx++] = END_CMD; 422 | transfer_size = resp_idx; 423 | 424 | } 425 | 426 | void CommLgc::setDNS(){ 427 | //TODO: To be tested 428 | int resp_idx = 2; 429 | bool result; 430 | uint8_t validParams = 0; 431 | 432 | validParams = _reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 433 | 434 | uint8_t dns1ip0, dns1ip1, dns1ip2, dns1ip3, 435 | dns2ip0, dns2ip1, dns2ip2, dns2ip3; 436 | 437 | //retrieve the dns 1 address 438 | dns1ip0 = _reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_0]; 439 | dns1ip1 = _reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_1]; 440 | dns1ip2 = _reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_2]; 441 | dns1ip3 = _reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_3]; 442 | IPAddress dns1(dns1ip0, dns1ip1, dns1ip2, dns1ip3); 443 | 444 | //retrieve the dns 2 address 445 | dns2ip0 = _reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_0]; 446 | dns2ip1 = _reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_1]; 447 | dns2ip2 = _reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_2]; 448 | dns2ip3 = _reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_3]; 449 | IPAddress dns2(dns2ip0, dns2ip1, dns2ip2, dns2ip3); 450 | 451 | result = WiFi.config(*_handyIp, *_handyGateway, *_handySubnet, dns1, dns2); 452 | 453 | _resPckt[resp_idx++] = PARAM_NUMS_1; 454 | _resPckt[resp_idx++] = PARAM_SIZE_1; 455 | _resPckt[resp_idx++] = result; 456 | _resPckt[resp_idx++] = END_CMD; 457 | transfer_size = resp_idx; 458 | 459 | } 460 | 461 | void CommLgc::reqHostByName(){ 462 | //TODO to be tested 463 | int8_t resp_idx = 2; 464 | int result; 465 | char host[_reqPckt.params[PARAM_NUMS_0].paramLen]; 466 | //get the host name to look up 467 | //strncpy(host, _reqPckt.params[0].param, _reqPckt.params[0].paramLen); 468 | memcpy(host, _reqPckt.params[PARAM_NUMS_0].param, _reqPckt.params[PARAM_NUMS_0].paramLen); 469 | host[_reqPckt.params[PARAM_NUMS_0].paramLen] = '\0'; 470 | 471 | result = WiFi.hostByName(host, _reqHostIp); //retrieve the ip address of the host 472 | 473 | //set the response struct 474 | _resPckt[resp_idx++] = PARAM_NUMS_1; 475 | _resPckt[resp_idx++] = PARAM_SIZE_1; 476 | _resPckt[resp_idx++] = result; 477 | _resPckt[resp_idx++] = END_CMD; 478 | transfer_size = resp_idx; 479 | 480 | } 481 | 482 | void CommLgc::getHostByName(){ 483 | //TODO to be tested 484 | int resp_idx = 2; 485 | //set the response struct 486 | _resPckt[resp_idx++] = PARAM_NUMS_1; 487 | _resPckt[resp_idx++] = PARAM_SIZE_4; 488 | //gets _reqHostIp (obtained before using reqHostByName) and send back to arduino 489 | _resPckt[resp_idx++] = _reqHostIp.operator[](0); 490 | _resPckt[resp_idx++] = _reqHostIp.operator[](1); 491 | _resPckt[resp_idx++] = _reqHostIp.operator[](2); 492 | _resPckt[resp_idx++] = _reqHostIp.operator[](3); 493 | _resPckt[resp_idx++] = END_CMD; 494 | transfer_size = resp_idx; 495 | 496 | } 497 | 498 | void CommLgc::getFwVersion(){ 499 | 500 | int resp_idx = 2; 501 | //send back to arduino the firmware version number 502 | _resPckt[resp_idx++] = PARAM_NUMS_1; 503 | _resPckt[resp_idx++] = PARAM_SIZE_5; 504 | //set fw version 505 | memcpy(_resPckt+(resp_idx), FW_VERSION, PARAM_SIZE_5); 506 | resp_idx = resp_idx + PARAM_SIZE_5; 507 | _resPckt[resp_idx++] = END_CMD; 508 | transfer_size = resp_idx; 509 | 510 | } 511 | 512 | /* WiFi IPAddress*/ 513 | void CommLgc::getNetworkData(){ 514 | //TODO to be tested 515 | int resp_idx = 2; 516 | IPAddress localIp, subnetMask, gatewayIp;//, dnsIp1, dnsIp2; 517 | 518 | localIp = WiFi.localIP(); 519 | subnetMask = WiFi.subnetMask(); 520 | gatewayIp = WiFi.gatewayIP(); 521 | 522 | //set the response struct 523 | _resPckt[resp_idx++] = PARAM_NUMS_3; 524 | _resPckt[resp_idx++] = PARAM_SIZE_4; 525 | 526 | _resPckt[resp_idx++] = localIp.operator[](0); 527 | _resPckt[resp_idx++] = localIp.operator[](1); 528 | _resPckt[resp_idx++] = localIp.operator[](2); 529 | _resPckt[resp_idx++] = localIp.operator[](3); 530 | 531 | _resPckt[resp_idx++] = PARAM_SIZE_4; 532 | _resPckt[resp_idx++] = subnetMask.operator[](0); 533 | _resPckt[resp_idx++] = subnetMask.operator[](1); 534 | _resPckt[resp_idx++] = subnetMask.operator[](2); 535 | _resPckt[resp_idx++] = subnetMask.operator[](3); 536 | 537 | _resPckt[resp_idx++] = PARAM_SIZE_4; 538 | _resPckt[resp_idx++] = gatewayIp.operator[](0); 539 | _resPckt[resp_idx++] = gatewayIp.operator[](1); 540 | _resPckt[resp_idx++] = gatewayIp.operator[](2); 541 | _resPckt[resp_idx++] = gatewayIp.operator[](3); 542 | _resPckt[resp_idx++] = END_CMD; 543 | transfer_size = resp_idx; 544 | 545 | } 546 | 547 | /* WiFI Server */ 548 | void CommLgc::startServer(){ 549 | //TODO: To be tested 550 | int resp_idx = 2; 551 | uint8_t result = 0; 552 | uint16_t _port = 0; 553 | int _sock = 0; 554 | uint8_t _prot = 0; 555 | 556 | //retrieve the port to start server 557 | uint8_t _p1 = (uint8_t)_reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 558 | uint8_t _p2 = (uint8_t)_reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_1]; 559 | _port = (_p1 << 8) + _p2; 560 | //retrieve sockets number 561 | _sock = (int)_reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_0]; 562 | 563 | //retrieve protocol mode (TCP/UDP) 564 | _prot = (uint8_t)_reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_0]; 565 | if(_sock < MAX_SOCK_NUMBER) { 566 | if(_prot == 0){ //TCP MODE 567 | if(mapWiFiServers[_sock] != NULL ){ 568 | //mapWiFiServers[_sock]->stop(); 569 | mapWiFiServers[_sock]->close(); 570 | free(mapWiFiServers[_sock]); 571 | } 572 | if(_port == 80){ 573 | //UIserver.stop(); //stop UI SERVER 574 | UI_alert = true; 575 | } 576 | mapWiFiServers[_sock] = new WiFiServer(_port); 577 | mapWiFiServers[_sock]->begin(); 578 | result = 1; 579 | } else { //UDP MODE 580 | if(mapWiFiUDP[_sock] != NULL ){ 581 | //TODO: stop, close? 582 | } else { 583 | 584 | mapWiFiUDP[_sock].begin(_port); 585 | result = 1; 586 | } 587 | } 588 | } 589 | //set the response struct 590 | _resPckt[resp_idx++] = PARAM_NUMS_1; 591 | _resPckt[resp_idx++] = PARAM_SIZE_1; 592 | _resPckt[resp_idx++] = result; 593 | _resPckt[resp_idx++] = END_CMD; 594 | transfer_size = resp_idx; 595 | 596 | } 597 | 598 | void CommLgc::availData(){ 599 | //TODO to be tested 600 | int resp_idx = 2; 601 | uint16_t result = 0; 602 | uint8_t _sock = 0; 603 | 604 | //retrieve socket index 605 | _sock = (uint8_t)_reqPckt.params[0].param[0]; 606 | 607 | if(_sock < MAX_SOCK_NUMBER) { 608 | if(mapWiFiClients[_sock]){ //mapWiFiClients[_sock] != NULL 609 | result = mapWiFiClients[_sock].available(); 610 | } 611 | else if(mapWiFiUDP[_sock] != NULL){//non ho un riferimento sul protocollo, quindi uso l'indice di socket 612 | result = mapWiFiUDP[_sock].parsePacket(); 613 | } 614 | } 615 | 616 | //set the response struct 617 | _resPckt[resp_idx++] = PARAM_NUMS_1; 618 | _resPckt[resp_idx++] = PARAM_SIZE_2; 619 | _resPckt[resp_idx++] = ((uint8_t*)&result)[0]; 620 | // _resPckt[4] = (uint8_t)((result & 0xff00)>>8);//((uint8_t*)&bufferSize)[1]; 621 | // _resPckt[5] = (uint8_t)(result & 0xff); 622 | _resPckt[resp_idx++] = ((uint8_t*)&result)[1]; 623 | _resPckt[resp_idx++] = END_CMD; 624 | transfer_size = resp_idx; 625 | bufferSize = result; 626 | } 627 | 628 | void CommLgc::serverStatus(){ 629 | //TODO: To be tested 630 | int resp_idx = 2; 631 | uint8_t result = 0; 632 | uint8_t _sock = 0; 633 | 634 | //retrieve socket index 635 | _sock = (uint8_t)_reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 636 | if(mapWiFiServers[_sock] != NULL) 637 | result = mapWiFiServers[_sock]->status(); 638 | else 639 | result =0; 640 | //set the response struct 641 | _resPckt[resp_idx++] = PARAM_NUMS_1; 642 | _resPckt[resp_idx++] = PARAM_SIZE_1; 643 | _resPckt[resp_idx++] = result; 644 | _resPckt[resp_idx++] = END_CMD; 645 | transfer_size = resp_idx; 646 | //Serial.print("3"); 647 | 648 | } 649 | 650 | void CommLgc::getData(){ 651 | //TODO: To be tested 652 | int resp_idx = 2; 653 | uint8_t result = 0; 654 | uint8_t _sock = 0; 655 | uint8_t _peek = 0; 656 | 657 | //retrieve socket index 658 | _sock = (uint8_t)_reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 659 | 660 | //retrieve peek 661 | _peek = (uint8_t)_reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_0]; 662 | 663 | if(mapWiFiClients[_sock]){ 664 | if(_peek > 0){ 665 | result = mapWiFiClients[_sock].peek(); 666 | } 667 | else{ 668 | result = mapWiFiClients[_sock].read(); 669 | } 670 | } 671 | else if(mapWiFiUDP[_sock] != NULL){ 672 | if(_peek > 0){ 673 | result = mapWiFiUDP[_sock].peek(); 674 | } 675 | else{ 676 | result = mapWiFiUDP[_sock].read(); 677 | } 678 | } 679 | _resPckt[resp_idx++] = PARAM_NUMS_1; 680 | _resPckt[resp_idx++] = PARAM_SIZE_1; 681 | _resPckt[resp_idx++] = result; 682 | _resPckt[resp_idx++] = END_CMD; 683 | transfer_size = resp_idx; 684 | 685 | } 686 | 687 | /* WiFi Client */ 688 | void CommLgc::stopClient(){ 689 | //TODO to be tested 690 | int resp_idx = 2; 691 | uint8_t result = 0; 692 | uint8_t _sock = 0; 693 | _sock = (uint8_t)_reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 694 | if(_sock < MAX_SOCK_NUMBER){ 695 | if(mapWiFiClients[_sock]){ //!= NULL 696 | mapWiFiClients[_sock].stop(); 697 | //mapWiFiClients[_sock] = NULL; 698 | result = 1; 699 | } 700 | else if(mapWiFiUDP[_sock] != NULL){ 701 | mapWiFiUDP[_sock].stop(); 702 | result = 1; 703 | } 704 | } 705 | 706 | //set the response struct 707 | _resPckt[resp_idx++] = PARAM_NUMS_1; 708 | _resPckt[resp_idx++] = PARAM_SIZE_1; 709 | _resPckt[resp_idx++] = result; 710 | _resPckt[resp_idx++] = END_CMD; 711 | transfer_size = resp_idx; 712 | 713 | } 714 | 715 | void CommLgc::clientStatus(){ 716 | //TODO to be tested 717 | int resp_idx = 2; 718 | uint8_t result = 0; 719 | uint8_t _sock = 0; //socket index 720 | _sock = (uint8_t)_reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 721 | if(_sock < MAX_SOCK_NUMBER) { 722 | if(!mapWiFiClients[_sock]){ //mapWiFiClients[_sock] == NULL 723 | if(mapWiFiServers[_sock]!= NULL){ 724 | mapWiFiClients[_sock] = mapWiFiServers[_sock]->available(); //Create the client from the server [Arduino as a Server] 725 | result = mapWiFiClients[_sock].status(); 726 | } 727 | }else { 728 | result = mapWiFiClients[_sock].status(); 729 | } 730 | } 731 | 732 | //set the response struct 733 | _resPckt[resp_idx++] = PARAM_NUMS_1; 734 | _resPckt[resp_idx++] = PARAM_SIZE_1; 735 | _resPckt[resp_idx++] = result; 736 | _resPckt[resp_idx++] = END_CMD; 737 | transfer_size = resp_idx; 738 | 739 | } 740 | 741 | void CommLgc::sendData(){ 742 | //TODO to be tested 743 | int resp_idx = 2; 744 | int result = 0; 745 | uint8_t _sock = 0; //socket index 746 | 747 | _sock = (uint8_t)_reqPckt.paramsData[PARAM_NUMS_0].data[PARAM_NUMS_0]; 748 | if(mapWiFiClients[_sock]){ 749 | if(mapWiFiClients[_sock].status()== 4) //TODO 750 | result = mapWiFiClients[_sock].write(_reqPckt.paramsData[PARAM_NUMS_1].data,_reqPckt.paramsData[PARAM_NUMS_1].dataLen); 751 | if(result == _reqPckt.paramsData[PARAM_NUMS_1].dataLen) 752 | tcpResult = 1; 753 | else 754 | tcpResult = 0; 755 | } 756 | _resPckt[resp_idx++] = PARAM_NUMS_1; 757 | _resPckt[resp_idx++] = PARAM_SIZE_1; 758 | _resPckt[resp_idx++] = tcpResult; //tcpResult 759 | _resPckt[resp_idx++] = END_CMD; 760 | transfer_size = resp_idx; 761 | 762 | } 763 | 764 | void CommLgc::checkDataSent(){ 765 | 766 | int resp_idx = 2; 767 | //set the response struct 768 | _resPckt[resp_idx++] = PARAM_NUMS_1; 769 | _resPckt[resp_idx++] = PARAM_SIZE_1; 770 | _resPckt[resp_idx++] = tcpResult; 771 | _resPckt[resp_idx++] = END_CMD; 772 | transfer_size = resp_idx; 773 | 774 | } 775 | 776 | void CommLgc::startClient(){ 777 | //TODO to be tested 778 | int resp_idx = 2; 779 | int result = 0; 780 | int _sock; 781 | uint16_t _port; 782 | uint8_t _prot; 783 | 784 | //retrieve the IP address to connect to 785 | uint8_t stip1 = _reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 786 | uint8_t stip2 = _reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_1]; 787 | uint8_t stip3 = _reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_2]; 788 | uint8_t stip4 = _reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_3]; 789 | IPAddress _ip(stip1, stip2, stip3, stip4); 790 | 791 | //retrieve the port to connect to 792 | uint8_t _p1 = (uint8_t)_reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_0]; 793 | uint8_t _p2 = (uint8_t)_reqPckt.params[PARAM_NUMS_1].param[PARAM_NUMS_1]; 794 | _port = (_p1 << 8) + _p2; 795 | 796 | //retrieve sockets number 797 | _sock = (int)_reqPckt.params[PARAM_NUMS_2].param[PARAM_NUMS_0]; 798 | 799 | //retrieve protocol mode (TCP/UDP) 800 | _prot = (uint8_t)_reqPckt.params[PARAM_NUMS_3].param[PARAM_NUMS_0]; 801 | 802 | if(_sock < MAX_SOCK_NUMBER) { 803 | if(_prot == 0){ 804 | result = mapWiFiClients[_sock].connect(_ip, _port); 805 | } else { 806 | result = mapWiFiUDP[_sock].beginPacket(_ip, _port); 807 | } 808 | } 809 | //set the response struct 810 | _resPckt[resp_idx++] = PARAM_NUMS_1; 811 | _resPckt[resp_idx++] = PARAM_SIZE_1; 812 | _resPckt[resp_idx++] = result; 813 | _resPckt[resp_idx++] = END_CMD; 814 | transfer_size = resp_idx; 815 | } 816 | 817 | /* WiFi UDP Client */ 818 | void CommLgc::remoteData(){ 819 | //TODO to be tested 820 | int resp_idx = 2; 821 | int _sock; 822 | 823 | //retrieve sockets number 824 | _sock = (int)_reqPckt.params[PARAM_NUMS_0].param[PARAM_NUMS_0]; 825 | 826 | if(_sock < MAX_SOCK_NUMBER && mapWiFiUDP[_sock] != NULL) { 827 | //set the response struct 828 | _resPckt[resp_idx++] = PARAM_NUMS_2; 829 | _resPckt[resp_idx++] = PARAM_SIZE_4; 830 | 831 | IPAddress remoteIp = mapWiFiUDP[_sock].remoteIP(); 832 | _resPckt[resp_idx++] = remoteIp.operator[](0); 833 | _resPckt[resp_idx++] = remoteIp.operator[](1); 834 | _resPckt[resp_idx++] = remoteIp.operator[](2); 835 | _resPckt[resp_idx++] = remoteIp.operator[](3); 836 | 837 | uint16_t remotePort = mapWiFiUDP[_sock].remotePort(); 838 | _resPckt[resp_idx++] = PARAM_SIZE_2; 839 | _resPckt[resp_idx++] = (uint8_t)((remotePort & 0xff00)>>8); 840 | _resPckt[resp_idx++] = (uint8_t)(remotePort & 0xff); 841 | _resPckt[resp_idx++] = END_CMD; 842 | transfer_size = resp_idx; 843 | 844 | } else { 845 | createErrorResponse(); 846 | } 847 | } 848 | 849 | void CommLgc::getDataBuf(){ 850 | //TODO: To be tested 851 | int resp_idx = 2; 852 | int result = 0; 853 | uint8_t _sock = 0; 854 | _sock = (uint8_t)_reqPckt.paramsData[PARAM_NUMS_0].data[PARAM_NUMS_0]; 855 | if (_reqPckt.nParam == 2) { // requested size 856 | uint16_t size = ((uint16_t) _reqPckt.paramsData[1].data[0] << 8) + _reqPckt.paramsData[1].data[1]; 857 | if (size < bufferSize) { 858 | bufferSize = size; 859 | } 860 | } 861 | 862 | // if(bufferSize>RESPONSE_LENGHT-6) 863 | // bufferSize= RESPONSE_LENGHT-6; //fix max length for UDP packet 864 | // if(bufferSize > 26) 865 | // _resPckt_len = ceil((float)bufferSize/32); 866 | // else 867 | // _resPckt_len = 0; 868 | if(_sock < MAX_SOCK_NUMBER){ 869 | if(mapWiFiUDP[_sock] != NULL){ 870 | char buffer[bufferSize+1]; //bufferSize is filled before by availData 871 | result = mapWiFiUDP[_sock].read(buffer, bufferSize); 872 | _resPckt[resp_idx++] = PARAM_NUMS_1; 873 | _resPckt[resp_idx++] = (uint8_t)((bufferSize & 0xff00)>>8);//((uint8_t*)&bufferSize)[1]; 874 | _resPckt[resp_idx++] = (uint8_t)(bufferSize & 0xff); 875 | memcpy(_resPckt+resp_idx,buffer,bufferSize); 876 | resp_idx = resp_idx + bufferSize; 877 | _resPckt[resp_idx++] = END_CMD; 878 | transfer_size = resp_idx; 879 | 880 | } 881 | else if(mapWiFiClients[_sock]){ 882 | 883 | uint8_t buffer_tcp[bufferSize+1]; 884 | bufferSize = mapWiFiClients[_sock].read(buffer_tcp, bufferSize); 885 | //TODO need to add a buffer 886 | _resPckt[resp_idx++] = PARAM_NUMS_1; 887 | _resPckt[resp_idx++] = (uint8_t)((bufferSize & 0xff00)>>8);//((uint8_t*)&bufferSize)[1]; 888 | _resPckt[resp_idx++] = (uint8_t)(bufferSize & 0xff); 889 | memcpy(_resPckt+resp_idx,buffer_tcp,bufferSize); 890 | resp_idx = resp_idx + bufferSize; 891 | _resPckt[resp_idx++] = END_CMD; 892 | transfer_size = resp_idx; 893 | bufferSize = mapWiFiClients[_sock].available(); 894 | } 895 | } 896 | } 897 | 898 | void CommLgc::insDataBuf(){ 899 | //TODO: To be tested 900 | 901 | //NOTE maybe can use sendData, it's similar to this except the UDP 902 | int resp_idx = 2; 903 | uint8_t result = 0; 904 | uint8_t _sock = 0; 905 | 906 | //retrieve socket index 907 | _sock = (uint8_t)_reqPckt.paramsData[PARAM_NUMS_0].data[PARAM_NUMS_0]; 908 | if(_sock < MAX_SOCK_NUMBER && mapWiFiUDP[_sock] != NULL){ 909 | mapWiFiUDP[_sock].write(_reqPckt.paramsData[PARAM_NUMS_1].data, _reqPckt.paramsData[PARAM_NUMS_1].dataLen); 910 | result = 1; 911 | } 912 | 913 | //set the response struct 914 | _resPckt[resp_idx++] = PARAM_NUMS_1; 915 | _resPckt[resp_idx++] = PARAM_SIZE_1; 916 | _resPckt[resp_idx++] = result; 917 | _resPckt[resp_idx++] = END_CMD; 918 | transfer_size = resp_idx; 919 | 920 | } 921 | 922 | void CommLgc::sendUdpData(){ 923 | //TODO: To be tested 924 | int resp_idx = 2; 925 | int result = 0; 926 | uint8_t _sock = 0; 927 | 928 | //retrieve socket index 929 | _sock = (uint8_t)_reqPckt.paramsData[PARAM_NUMS_0].data[PARAM_NUMS_0]; 930 | 931 | if(_sock < MAX_SOCK_NUMBER && mapWiFiUDP[_sock] != NULL){ 932 | //send data to client 933 | result = mapWiFiUDP[_sock].endPacket(); 934 | } 935 | //set the response struct 936 | _resPckt[resp_idx++] = PARAM_NUMS_1; 937 | _resPckt[resp_idx++] = PARAM_SIZE_1; 938 | _resPckt[resp_idx++] = result; 939 | _resPckt[resp_idx++] = END_CMD; 940 | transfer_size = resp_idx; 941 | 942 | } 943 | 944 | CommLgc CommunicationLogic; 945 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/CommLgc.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright <2017> 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | DEALINGS IN THE SOFTWARE 21 | 22 | */ 23 | #include "CommItf.h" 24 | #include "utility/wifi_utils.h" 25 | #include 26 | //#include 27 | #include 28 | #include "Arduino.h" 29 | 30 | #ifndef H_COMM_LGC_H 31 | #define H_COMM_LGC_H 32 | 33 | // #define MAX_MODE_NUM 2 34 | // #define MAP_TCP_MODE 1 35 | 36 | class CommLgc { 37 | 38 | public: 39 | CommLgc(); 40 | bool UI_alert = false; 41 | 42 | /* Logic Functions */ 43 | void begin(); 44 | void handle(); 45 | 46 | /* Commands Functions */ 47 | private: 48 | 49 | /* WiFi Communication */ 50 | void createErrorResponse(); 51 | void process(); 52 | 53 | void resetESP(); 54 | 55 | /* WiFi Base */ 56 | void getCurrentSSID(); 57 | void getRSSI(uint8_t current); 58 | void getEncryption(uint8_t current); 59 | void getMacAddress(); 60 | void disconnect(); 61 | void getStatus(); 62 | void begin(uint8_t current); 63 | void startScanNetwork(); 64 | void scanNetwork(); 65 | void getBSSID(uint8_t current); 66 | void config(); 67 | void setDNS(); 68 | void reqHostByName(); 69 | void getHostByName(); 70 | void getFwVersion(); 71 | void getNetworkData(); 72 | 73 | /* WiFi Server */ 74 | void startServer(); 75 | void availData(); 76 | void serverStatus(); 77 | void getData(); 78 | void sendData(); 79 | void checkDataSent(); 80 | 81 | /* WiFi Client */ 82 | void startClient(); 83 | void stopClient(); 84 | void clientStatus(); 85 | 86 | /* WiFI UDP Client */ 87 | void remoteData(); 88 | void getDataBuf(); 89 | void insDataBuf(); 90 | void sendUdpData(); 91 | 92 | }; 93 | 94 | extern CommLgc CommunicationLogic; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/Configuration.cpp: -------------------------------------------------------------------------------- 1 | #include "Configuration.h" 2 | 3 | 4 | bool Configuration::setParam(String param, String value){ 5 | if(value.length()>1 && param.length()>1){ 6 | String paramConfig = ""; 7 | DynamicJsonBuffer jsonBuffer; 8 | File configFile = SPIFFS.open(CONFIGFILENAME,"r"); 9 | if(configFile){ //read Config file 10 | while(configFile.available()){ 11 | paramConfig = paramConfig + char(configFile.read()); 12 | } 13 | } 14 | else{ 15 | paramConfig = JSONEMPTY; 16 | } 17 | configFile.close(); 18 | JsonObject& json = jsonBuffer.parseObject(paramConfig); 19 | //write param to config file 20 | configFile = SPIFFS.open(CONFIGFILENAME, "w"); 21 | json[param] = value; 22 | json.printTo(configFile); 23 | configFile.close(); 24 | return true; 25 | } 26 | else{ 27 | return false; 28 | } 29 | } 30 | 31 | String Configuration::getParam(String param){ 32 | if(param.length()>1){ 33 | File configFile = SPIFFS.open(CONFIGFILENAME, "r"); 34 | if (!configFile) { 35 | return EMPTY; 36 | } 37 | size_t size = configFile.size(); 38 | std::unique_ptr buf(new char[size]); 39 | configFile.readBytes(buf.get(), size); 40 | 41 | DynamicJsonBuffer jsonBuffer; 42 | JsonObject& json = jsonBuffer.parseObject(buf.get()); 43 | 44 | if (!json.success()) { 45 | return EMPTY; 46 | } 47 | configFile.close(); 48 | return json[param]; 49 | } 50 | else{ 51 | return EMPTY; 52 | } 53 | } 54 | 55 | Configuration Config; 56 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/Configuration.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright <2017> 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | DEALINGS IN THE SOFTWARE 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #ifndef H_CONFIGURATION_H 29 | #define H_CONFIGURATION_H 30 | 31 | #define EMPTY "" 32 | #define JSONEMPTY "{}" 33 | #define MAXVALUESIZE 256 34 | #define CONFIGFILENAME "/config.json" 35 | 36 | class Configuration { 37 | 38 | public: 39 | bool setParam(String param, String value); 40 | String getParam(String param); 41 | 42 | }; 43 | 44 | extern Configuration Config; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/WebServer.ino: -------------------------------------------------------------------------------- 1 | // uint8_t MAC_array[6]; //mac_address 2 | // char macAddress[12]; //mac_address 3 | String newSSID_param; 4 | String newPASSWORD_param; 5 | const char* newSSID_par = ""; 6 | const char* newPASSWORD_par = ""; 7 | // IPAddress default_IP(192,168,240,1); //defaul IP Address 8 | bool SERVER_STOP = false; //check stop server 9 | 10 | // extern "C" void system_set_os_print(uint8 onoff); //TODO to test without 11 | // extern "C" void ets_install_putc1(void* routine); 12 | int tot = 0; 13 | bool connect_wifi = false; 14 | //String HOSTNAME = "arduino"; 15 | 16 | 17 | String getContentType(String filename){ 18 | if(server.hasArg("download")) return "application/octet-stream"; 19 | else if(filename.endsWith(".htm")) return "text/html"; 20 | else if(filename.endsWith(".html")) return "text/html"; 21 | else if(filename.endsWith(".css")) return "text/css"; 22 | else if(filename.endsWith(".js")) return "application/javascript"; 23 | else if(filename.endsWith(".png")) return "image/png"; 24 | else if(filename.endsWith(".gif")) return "image/gif"; 25 | else if(filename.endsWith(".jpg")) return "image/jpeg"; 26 | else if(filename.endsWith(".ico")) return "image/x-icon"; 27 | else if(filename.endsWith(".xml")) return "text/xml"; 28 | else if(filename.endsWith(".pdf")) return "application/x-pdf"; 29 | else if(filename.endsWith(".zip")) return "application/x-zip"; 30 | else if(filename.endsWith(".gz")) return "application/x-gzip"; 31 | return "text/plain"; 32 | } 33 | 34 | bool handleFileRead(String path){ 35 | if(path.endsWith("/")) path += "index.html"; 36 | String contentType = getContentType(path); 37 | File file = SPIFFS.open(path, "r"); 38 | if (file == NULL) 39 | return false; 40 | size_t sent = server.streamFile(file, contentType); 41 | file.close(); 42 | return true; 43 | } 44 | 45 | String toStringIp(IPAddress ip) { 46 | String res = ""; 47 | for (int i = 0; i < 3; i++) { 48 | res += String((ip >> (8 * i)) & 0xFF) + "."; 49 | } 50 | res += String(((ip >> 8 * 3)) & 0xFF); 51 | return res; 52 | } 53 | 54 | String toStringWifiMode(int mod) { 55 | String mode; 56 | switch (mod) { 57 | case 0: 58 | mode = "OFF"; 59 | break; 60 | case 1: 61 | mode = "STA"; 62 | break; 63 | case 2: 64 | mode = "AP"; 65 | break; 66 | case 3: 67 | mode = "AP+STA"; 68 | break; 69 | case 4: 70 | mode = "----"; 71 | break; 72 | default: 73 | break; 74 | } 75 | return mode; 76 | } 77 | 78 | WiFiMode intToWifiMode(int mod) { 79 | WiFiMode mode; 80 | switch (mod) { 81 | case 0: 82 | mode = WIFI_OFF; 83 | break; 84 | case 1: 85 | mode = WIFI_STA; 86 | break; 87 | case 2: 88 | mode = WIFI_AP; 89 | break; 90 | case 3: 91 | mode = WIFI_AP_STA; 92 | break; 93 | case 4: 94 | break; 95 | default: 96 | break; 97 | } 98 | return mode; 99 | } 100 | 101 | String toStringWifiStatus(int state) { 102 | String status; 103 | switch (state) { 104 | case 0: 105 | status = "connecting"; 106 | break; 107 | case 1: 108 | status = "unknown status"; 109 | break; 110 | case 2: 111 | status = "wifi scan completed"; 112 | break; 113 | case 3: 114 | status = "got IP address"; 115 | // statements 116 | break; 117 | case 4: 118 | status = "connection failed"; 119 | break; 120 | default: 121 | break; 122 | } 123 | return status; 124 | } 125 | 126 | String toStringEncryptionType(int thisType) { 127 | String eType; 128 | switch (thisType) { 129 | case ENC_TYPE_WEP: 130 | eType = "WEP"; 131 | break; 132 | case ENC_TYPE_TKIP: 133 | eType = "WPA"; 134 | break; 135 | case ENC_TYPE_CCMP: 136 | eType = "WPA2"; 137 | break; 138 | case ENC_TYPE_NONE: 139 | eType = "None"; 140 | break; 141 | case ENC_TYPE_AUTO: 142 | eType = "Auto"; 143 | break; 144 | } 145 | return eType; 146 | } 147 | 148 | IPAddress stringToIP(String address) { 149 | int p1 = address.indexOf('.'), p2 = address.indexOf('.', p1 + 1), p3 = address.indexOf('.', p2 + 1); //, 4p = address.indexOf(3p+1); 150 | String ip1 = address.substring(0, p1), ip2 = address.substring(p1 + 1, p2), ip3 = address.substring(p2 + 1, p3), ip4 = address.substring(p3 + 1); 151 | 152 | return IPAddress(ip1.toInt(), ip2.toInt(), ip3.toInt(), ip4.toInt()); 153 | } 154 | 155 | void handleWebServer(){ 156 | if(connect_wifi){ 157 | ETS_SPI_INTR_DISABLE(); 158 | WiFi.begin(newSSID_param.c_str(),newPASSWORD_param.c_str()); 159 | delay(500); // VB: exactly 500, and here! 160 | connect_wifi = false; 161 | ETS_SPI_INTR_ENABLE(); 162 | } 163 | if(CommunicationLogic.UI_alert){ //stop UI SERVER 164 | if(!SERVER_STOP){ 165 | server.stop(); 166 | SERVER_STOP = true; 167 | } 168 | } 169 | else{ 170 | server.handleClient(); 171 | } 172 | } 173 | 174 | void clearStaticIP() { 175 | dhcp = "on"; 176 | Config.setParam("staticIP", "0.0.0.0"); 177 | Config.setParam("netMask", "255.255.255.0"); 178 | Config.setParam("gatewayIP", "192.168.1.1"); 179 | } 180 | 181 | void initWebServer(){ 182 | 183 | tot = WiFi.scanNetworks(); 184 | 185 | //"wifi/info" information 186 | server.on("/wifi/info", []() { 187 | String ipadd = (WiFi.getMode() == 1 || WiFi.getMode() == 3) ? toStringIp(WiFi.localIP()) : toStringIp(WiFi.softAPIP()); 188 | String staticadd = dhcp.equals("on") ? "0.0.0.0" : staticIP_param; 189 | int change = WiFi.getMode() == 1 ? 3 : 1; 190 | String cur_ssid = (WiFi.getMode() == 2 )? "none" : WiFi.SSID(); 191 | 192 | 193 | server.send(200, "text/plain", String("{\"ssid\":\"" + cur_ssid + "\",\"hostname\":\"" + WiFi.hostname() + "\",\"ip\":\"" + ipadd + "\",\"mode\":\"" + toStringWifiMode(WiFi.getMode()) + "\",\"chan\":\"" 194 | + WiFi.channel() + "\",\"status\":\"" + toStringWifiStatus(WiFi.status()) + "\", \"gateway\":\"" + toStringIp(WiFi.gatewayIP()) + "\", \"netmask\":\"" + toStringIp(WiFi.subnetMask()) + "\",\"rssi\":\"" 195 | + WiFi.RSSI() + "\",\"mac\":\"" + WiFi.macAddress() + "\",\"phy\":\"" + WiFi.getPhyMode() + "\", \"dhcp\": \"" + dhcp + "\", \"staticip\":\"" + staticadd + 196 | + "\", \"warn\": \"" + "Switch to " + toStringWifiMode(change) + " mode\"" 197 | + "}" )); 198 | }); 199 | 200 | //"system/info" information 201 | server.on("/system/info", []() { 202 | server.send(200, "text/plain", String("{\"heap\":\""+ String(ESP.getFreeHeap()/1024)+" KB\",\"id\":\"" + String(ESP.getFlashChipId()) + "\",\"size\":\"" + (ESP.getFlashChipSize() / 1024 / 1024) + " MB\",\"baud\":\"9600\"}")); 203 | }); 204 | 205 | server.on("/heap", []() { 206 | server.send(200, "text/plain", String(ESP.getFreeHeap())); 207 | }); 208 | 209 | server.on("/system/update", []() { 210 | String newhostname = server.arg("name"); 211 | WiFi.hostname(newhostname); 212 | MDNS.begin(newhostname.c_str()); 213 | MDNS.setInstanceName(newhostname); 214 | server.send(200, "text/plain", newhostname); 215 | Config.setParam("hostname", newhostname); 216 | }); 217 | 218 | server.on("/wifi/netNumber", []() { 219 | tot = WiFi.scanNetworks(); 220 | server.send(200, "text/plain", String(tot)); 221 | }); 222 | 223 | server.on("/wifi/scan", []() { 224 | String scanResp = ""; 225 | if (tot == 0) { 226 | server.send(200, "text/plain", "No networks found"); 227 | } 228 | if (tot == -1 ) { 229 | server.send(500, "text/plain", "Error during scanning"); 230 | } 231 | 232 | scanResp += "{\"result\": { \"APs\" : [ "; 233 | //ETS_SPI_INTR_DISABLE(); 234 | for (int netIndex = 0; netIndex < tot; netIndex++) { 235 | scanResp += "{\"enc\" : \""; 236 | scanResp += toStringEncryptionType (WiFi.encryptionType(netIndex)); 237 | scanResp += "\","; 238 | scanResp += "\"essid\":\""; 239 | scanResp += WiFi.SSID(netIndex); 240 | scanResp += "\","; 241 | scanResp += "\"rssi\" :\""; 242 | scanResp += WiFi.RSSI(netIndex); 243 | scanResp += "\"}"; 244 | if (netIndex != tot - 1) 245 | scanResp += ","; 246 | } 247 | scanResp += "]}}"; 248 | //ETS_SPI_INTR_ENABLE(); 249 | server.send(200, "text/plain", scanResp); 250 | }); 251 | 252 | server.on("/connect", []() { 253 | newSSID_param = server.arg("essid"); 254 | newPASSWORD_param = server.arg("passwd"); 255 | server.send(200, "text/plain", "1"); 256 | clearStaticIP(); 257 | connect_wifi = true; 258 | }); 259 | 260 | server.on("/connstatus", []() { 261 | String ipadd = (WiFi.getMode() == 1 || WiFi.getMode() == 3) ? toStringIp(WiFi.localIP()) : toStringIp(WiFi.softAPIP()); 262 | server.send(200, "text/plain", String("{\"url\":\"got IP address\", \"ip\":\""+ipadd+"\", \"modechange\":\"no\", \"ssid\":\""+WiFi.SSID()+"\", \"reason\":\"-\", \"status\":\""+ toStringWifiStatus(WiFi.status()) +"\"}")); 263 | }); 264 | 265 | 266 | server.on("/setmode", []() { 267 | int newMode = server.arg("mode").toInt(); 268 | 269 | switch (newMode){ 270 | case 1 : 271 | case 3 : 272 | server.send(200, "text/plain", String("Mode changed " + toStringWifiMode(WiFi.getMode()))); 273 | WiFi.mode(intToWifiMode(newMode)); 274 | break; 275 | case 2 : 276 | server.send(200, "text/plain", String("Mode changed " + toStringWifiMode(WiFi.getMode()))); 277 | WiFi.mode(WIFI_AP); 278 | break; 279 | } 280 | 281 | }); 282 | 283 | server.on("/special", []() { 284 | dhcp = server.arg("dhcp"); 285 | staticIP_param = server.arg("staticip"); 286 | netmask_param = server.arg("netmask"); 287 | gateway_param = server.arg("gateway"); 288 | 289 | if (dhcp == "off") { 290 | server.send(200, "text/plain", String("{\"url\":\"" + staticIP_param + "\"}")); 291 | delay(500); // to let the response finish 292 | WiFi.config(stringToIP(staticIP_param), stringToIP(gateway_param), stringToIP(netmask_param)); 293 | Config.setParam("staticIP", staticIP_param); 294 | Config.setParam("netMask", netmask_param); 295 | Config.setParam("gatewayIP", gateway_param); 296 | } 297 | else { 298 | server.send(200, "text/plain", "1"); 299 | clearStaticIP(); 300 | ESP.restart(); 301 | } 302 | }); 303 | 304 | server.on("/boardInfo", []() { 305 | StaticJsonBuffer<200> jsonBuffer; 306 | JsonObject& boardInfo = jsonBuffer.createObject(); 307 | String output = ""; 308 | if (BOARDMODEL == "PRIMO"){ 309 | boardInfo["name"] = "Primo"; 310 | boardInfo["icon"] = "/img/logoPrimo.ico"; 311 | boardInfo["logo"] = "/img/logoPrimo.png"; 312 | } 313 | else if (BOARDMODEL == "STAROTTO"){ 314 | boardInfo["name"] = "Star Otto"; 315 | boardInfo["icon"] = "/img/logoOtto.ico"; 316 | boardInfo["logo"] = "/img/logoOtto.png"; 317 | } 318 | else if (BOARDMODEL =="UNOWIFIDEVED"){ 319 | boardInfo["name"] = "Uno WiFi"; 320 | boardInfo["icon"] = "/img/logoUnoWiFi.ico"; 321 | boardInfo["logo"] = "/img/logoUnoWiFi.png"; 322 | } else { 323 | boardInfo["name"] = "WiFi Link"; 324 | boardInfo["icon"] = "/img/logoUnoWiFi.ico"; 325 | boardInfo["logo"] = "/img/logoUnoWiFi.png"; 326 | } 327 | boardInfo["link"] = "https://github.com/jandrassy/arduino-library-wifilink#wifi-link-library"; 328 | 329 | boardInfo["fw_name"] = FW_NAME; 330 | boardInfo["fw_version"] = FW_VERSION; 331 | boardInfo["build_date"] = BUILD_DATE; 332 | 333 | boardInfo.printTo(output); 334 | server.send(200, "text/json", output); 335 | }); 336 | 337 | //called when the url is not defined here 338 | //use it to load content from SPIFFS 339 | server.onNotFound([](){ 340 | if(!handleFileRead(server.uri())) 341 | server.send(404, "text/plain", "FileNotFound"); 342 | }); 343 | 344 | server.begin(); 345 | delay(5); // VB: exactly 5, no more or less, no yield()! 346 | } 347 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/config.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Firmware version and build date 4 | */ 5 | 6 | #define BUILD_DATE __DATE__ " " __TIME__ 7 | #define FW_VERSION "1.1.0" 8 | #define FW_NAME "wifilink" 9 | 10 | /* 11 | * Define board model name 12 | */ 13 | 14 | #if defined(ARDUINO_ESP8266_ARDUINO_STAR_OTTO) 15 | #define STAROTTO 16 | #elif defined(ARDUINO_ESP8266_ARDUINO_PRIMO) 17 | #define PRIMO 18 | #elif defined(ARDUINO_ESP8266_ARDUINO_UNOWIFI) 19 | #define UNOWIFIDEVED 20 | //#define UNOWIFI // SPI version - not produced 21 | #else 22 | #define GENERIC_ESP8266 23 | #endif 24 | 25 | //#define MCU_OTA 26 | 27 | /* 28 | * Enable/Disable Debug 29 | */ 30 | 31 | //#define DEBUG 32 | //#define BAUDRATE_DEBUG 115200 33 | 34 | /* 35 | * Define board hostname 36 | */ 37 | 38 | #define DEF_HOSTNAME "arduino" 39 | 40 | /* 41 | * Defines the communication channel between microcontroller 42 | * and esp82266, with concerning parameters 43 | */ 44 | 45 | //#define HOSTNAME "arduino" //used by mdns protocol 46 | 47 | #if defined(STAROTTO) 48 | //Arduino STAR OTTO configuration parameters 49 | #define BOARDMODEL "STAROTTO" 50 | #define ARDUINO_BOARD "star_otto" //mdns 51 | #define ESP_CH_UART 52 | #define BAUDRATE_COMMUNICATION 460800 53 | #define WIFI_LED 14 54 | #define SSIDNAME "Arduino-Star-Otto" 55 | #elif defined(PRIMO) 56 | //Arduino PRIMO configuration parameters 57 | #define BOARDMODEL "PRIMO" 58 | #define ARDUINO_BOARD "primo" //mdns 59 | #define ESP_CH_SPI 60 | #define WIFI_LED 2 61 | #define SSIDNAME "Arduino-Primo" 62 | #elif defined(UNOWIFI) 63 | //Arduino UNOWIFI configuration parameters 64 | #define BOARDMODEL "UNOWIFI" 65 | #define ARDUINO_BOARD "unowifi" //mdns 66 | #define ESP_CH_SPI 67 | #define WIFI_LED 2 68 | #define SSIDNAME "Arduino-Uno-WiFi" 69 | #elif defined(UNOWIFIDEVED) 70 | //Arduino UNO WIFI DEV. EDITION configuration parameters 71 | #define BOARDMODEL "UNOWIFIDEVED" 72 | #define ARDUINO_BOARD "unowifi" //mdns 73 | #define ESP_CH_UART 74 | #define BAUDRATE_COMMUNICATION 115200 // assuming use of Serial1 75 | #define WIFI_LED 14 76 | #define SSIDNAME "Arduino-Uno-WiFi" 77 | #elif defined(GENERIC_ESP8266) 78 | #define BOARDMODEL "GENERIC_ESP8266" 79 | #define ARDUINO_BOARD "esp8266" //mdns 80 | #define ESP_CH_UART 81 | #define BAUDRATE_COMMUNICATION 9600 // to start with SoftwareSerial, set 115200 for good connection 82 | #define WIFI_LED 14 83 | #define SSIDNAME "ESP8266-Module" 84 | #endif 85 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/favicon.ico -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/icons.png -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/logo.ico -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/logo.png -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/logoOtto.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/logoOtto.ico -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/logoOtto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/logoOtto.png -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/logoPrimo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/logoPrimo.ico -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/logoPrimo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/logoPrimo.png -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/logoUnoWiFi.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/logoUnoWiFi.ico -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/img/logoUnoWiFi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JAndrassy/arduino-firmware-wifilink/c634eca7f34a117f0f3a8167f482636c74c616d1/ArduinoFirmwareEsp/data/img/logoUnoWiFi.png -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 |

Welcome to WiFi Link Firmware

18 |

Please refer to the online documentation for information about WiFi Link library.

19 |
20 |
21 |
22 |
23 |
24 |
25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | 40 | 41 | 42 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 78 | 79 | 80 | 81 |
30 |

Summary

Hostname 36 | 37 |
Network SSID 43 | 44 |
Wifi status
Wifi address
WiFi mode
WiFi channel
Flash size 69 | 70 |
Free heap 76 | 77 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/pure.css: -------------------------------------------------------------------------------- 1 | /*! 2 | Pure v0.6.0 3 | Copyright 2014 Yahoo! Inc. All rights reserved. 4 | Licensed under the BSD License. 5 | https://github.com/yahoo/pure/blob/master/LICENSE.md 6 | */ 7 | /*! 8 | normalize.css v^3.0 | MIT License | git.io/normalize 9 | Copyright (c) Nicolas Gallagher and Jonathan Neal 10 | */ 11 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 12 | 13 | html { 14 | font-family: sans-serif; 15 | -ms-text-size-adjust: 100%; 16 | -webkit-text-size-adjust: 100% 17 | } 18 | body { 19 | margin: 0 20 | } 21 | article, 22 | aside, 23 | details, 24 | figcaption, 25 | figure, 26 | footer, 27 | header, 28 | hgroup, 29 | main, 30 | menu, 31 | nav, 32 | section, 33 | summary { 34 | display: block 35 | } 36 | audio, 37 | canvas, 38 | progress, 39 | video { 40 | display: inline-block; 41 | vertical-align: baseline 42 | } 43 | audio:not([controls]) { 44 | display: none; 45 | height: 0 46 | } 47 | [hidden], 48 | template { 49 | display: none 50 | } 51 | a { 52 | background-color: transparent 53 | } 54 | a:active, 55 | a:hover { 56 | outline: 0 57 | } 58 | abbr[title] { 59 | border-bottom: 1px dotted 60 | } 61 | b, 62 | strong { 63 | font-weight: bold 64 | } 65 | dfn { 66 | font-style: italic 67 | } 68 | h1 { 69 | font-size: 2em; 70 | margin: .67em 0 71 | } 72 | mark { 73 | background: #ff0; 74 | color: #000 75 | } 76 | small { 77 | font-size: 80% 78 | } 79 | sub, 80 | sup { 81 | font-size: 75%; 82 | line-height: 0; 83 | position: relative; 84 | vertical-align: baseline 85 | } 86 | sup { 87 | top: -0.5em 88 | } 89 | sub { 90 | bottom: -0.25em 91 | } 92 | img { 93 | border: 0 94 | } 95 | svg:not(:root) { 96 | overflow: hidden 97 | } 98 | figure { 99 | margin: 1em 40px 100 | } 101 | hr { 102 | -moz-box-sizing: content-box; 103 | box-sizing: content-box; 104 | height: 0 105 | } 106 | pre { 107 | overflow: auto 108 | } 109 | code, 110 | kbd, 111 | pre, 112 | samp { 113 | font-family: monospace, monospace; 114 | font-size: 1em 115 | } 116 | button, 117 | input, 118 | optgroup, 119 | select, 120 | textarea { 121 | color: inherit; 122 | font: inherit; 123 | margin: 0 124 | } 125 | button { 126 | overflow: visible 127 | } 128 | button, 129 | select { 130 | text-transform: none 131 | } 132 | button, 133 | html input[type="button"], 134 | input[type="reset"], 135 | input[type="submit"] { 136 | -webkit-appearance: button; 137 | cursor: pointer 138 | } 139 | button[disabled], 140 | html input[disabled] { 141 | cursor: default 142 | } 143 | button::-moz-focus-inner, 144 | input::-moz-focus-inner { 145 | border: 0; 146 | padding: 0 147 | } 148 | input { 149 | line-height: normal 150 | } 151 | input[type="checkbox"], 152 | input[type="radio"] { 153 | box-sizing: border-box; 154 | padding: 0 155 | } 156 | input[type="number"]::-webkit-inner-spin-button, 157 | input[type="number"]::-webkit-outer-spin-button { 158 | height: auto 159 | } 160 | input[type="search"] { 161 | -webkit-appearance: textfield; 162 | -moz-box-sizing: content-box; 163 | -webkit-box-sizing: content-box; 164 | box-sizing: content-box 165 | } 166 | input[type="search"]::-webkit-search-cancel-button, 167 | input[type="search"]::-webkit-search-decoration { 168 | -webkit-appearance: none 169 | } 170 | fieldset { 171 | border: 1px solid silver; 172 | margin: 0 2px; 173 | padding: .35em .625em .75em 174 | } 175 | legend { 176 | border: 0; 177 | padding: 0 178 | } 179 | textarea { 180 | overflow: auto 181 | } 182 | optgroup { 183 | font-weight: bold 184 | } 185 | table { 186 | border-collapse: collapse; 187 | border-spacing: 0 188 | } 189 | td, 190 | th { 191 | padding: 0 192 | } 193 | .hidden, 194 | [hidden] { 195 | display: none !important 196 | } 197 | .pure-img { 198 | max-width: 100%; 199 | height: auto; 200 | display: block 201 | } 202 | .pure-g { 203 | letter-spacing: -0.31em; 204 | *letter-spacing: normal; 205 | *word-spacing: -0.43em; 206 | text-rendering: optimizespeed; 207 | font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif; 208 | display: -webkit-flex; 209 | -webkit-flex-flow: row wra 210 | /*! 211 | Pure v0.6.0 212 | Copyright 2014 Yahoo! Inc. All rights reserved. 213 | Licensed under the BSD License. 214 | https://github.com/yahoo/pure/blob/master/LICENSE.md 215 | */ 216 | /*! 217 | normalize.css v^3.0 | MIT License | git.io/normalize 218 | Copyright (c) Nicolas Gallagher and Jonathan Neal 219 | */ 220 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 221 | 222 | html { 223 | font-family: sans-serif; 224 | -ms-text-size-adjust: 100%; 225 | -webkit-text-size-adjust: 100% 226 | } 227 | body { 228 | margin: 0 229 | } 230 | article, 231 | aside, 232 | details, 233 | figcaption, 234 | figure, 235 | footer, 236 | header, 237 | hgroup, 238 | main, 239 | menu, 240 | nav, 241 | section, 242 | summary { 243 | display: block 244 | } 245 | audio, 246 | canvas, 247 | progress, 248 | video { 249 | display: inline-block; 250 | vertical-align: baseline 251 | } 252 | audio:not([controls]) { 253 | display: none; 254 | height: 0 255 | } 256 | [hidden], 257 | template { 258 | display: none 259 | } 260 | a { 261 | background-color: transparent 262 | } 263 | a:active, 264 | a:hover { 265 | outline: 0 266 | } 267 | abbr[title] { 268 | border-bottom: 1px dotted 269 | } 270 | b, 271 | strong { 272 | font-weight: bold 273 | } 274 | dfn { 275 | font-style: italic 276 | } 277 | h1 { 278 | font-size: 2em; 279 | margin: .67em 0 280 | } 281 | mark { 282 | background: #ff0; 283 | color: #000 284 | } 285 | small { 286 | font-size: 80% 287 | } 288 | sub, 289 | sup { 290 | font-size: 75%; 291 | line-height: 0; 292 | position: relative; 293 | vertical-align: baseline 294 | } 295 | sup { 296 | top: -0.5em 297 | } 298 | sub { 299 | bottom: -0.25em 300 | } 301 | img { 302 | border: 0 303 | } 304 | svg:not(:root) { 305 | overflow: hidden 306 | } 307 | figure { 308 | margin: 1em 40px 309 | } 310 | hr { 311 | -moz-box-sizing: content-box; 312 | box-sizing: content-box; 313 | height: 0 314 | } 315 | pre { 316 | overflow: auto 317 | } 318 | code, 319 | kbd, 320 | pre, 321 | samp { 322 | font-family: monospace, monospace; 323 | font-size: 1em 324 | } 325 | button, 326 | input, 327 | optgroup, 328 | select, 329 | textarea { 330 | color: inherit; 331 | font: inherit; 332 | margin: 0 333 | } 334 | button { 335 | overflow: visible 336 | } 337 | button, 338 | select { 339 | text-transform: none 340 | } 341 | button, 342 | html input[type="button"], 343 | input[type="reset"], 344 | input[type="submit"] { 345 | -webkit-appearance: button; 346 | cursor: pointer 347 | } 348 | button[disabled], 349 | html input[disabled] { 350 | cursor: default 351 | } 352 | button::-moz-focus-inner, 353 | input::-moz-focus-inner { 354 | border: 0; 355 | padding: 0 356 | } 357 | input { 358 | line-height: normal 359 | } 360 | input[type="checkbox"], 361 | input[type="radio"] { 362 | box-sizing: border-box; 363 | padding: 0 364 | } 365 | input[type="number"]::-webkit-inner-spin-button, 366 | input[type="number"]::-webkit-outer-spin-button { 367 | height: auto 368 | } 369 | input[type="search"] { 370 | -webkit-appearance: textfield; 371 | -moz-box-sizing: content-box; 372 | -webkit-box-sizing: content-box; 373 | box-sizing: content-box 374 | } 375 | input[type="search"]::-webkit-search-cancel-button, 376 | input[type="search"]::-webkit-search-decoration { 377 | -webkit-appearance: none 378 | } 379 | fieldset { 380 | border: 1px solid silver; 381 | margin: 0 2px; 382 | padding: .35em .625em .75em 383 | } 384 | legend { 385 | border: 0; 386 | padding: 0 387 | } 388 | textarea { 389 | overflow: auto 390 | } 391 | optgroup { 392 | font-weight: bold 393 | } 394 | table { 395 | border-collapse: collapse; 396 | border-spacing: 0 397 | } 398 | td, 399 | th { 400 | padding: 0 401 | } 402 | .hidden, 403 | [hidden] { 404 | display: none !important 405 | } 406 | .pure-img { 407 | max-width: 100%; 408 | height: auto; 409 | display: block 410 | } 411 | .pure-g { 412 | letter-spacing: -0.31em; 413 | *letter-spacing: normal; 414 | *word-spacing: -0.43em; 415 | text-rendering: optimizespeed; 416 | font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif; 417 | display: -webkit-flex; 418 | -webkit-flex-flow: row wrap; 419 | display: -ms-flexbox; 420 | -ms-flex-flow: row wrap; 421 | -ms-align-content: flex-start; 422 | -webkit-align-content: flex-start; 423 | align-content: flex-start 424 | } 425 | .opera-only:-o-prefocus, 426 | .pure-g { 427 | word-spacing: -0.43em 428 | } 429 | .pure-u { 430 | display: inline-block; 431 | *display: inline; 432 | zoom: 1; 433 | letter-spacing: normal; 434 | word-spacing: normal; 435 | vertical-align: top; 436 | text-rendering: auto 437 | } 438 | .pure-g [class * "pure-u"] { 439 | font-family: sans-serif 440 | } 441 | .pure-u-1, 442 | .pure-u-1-1, 443 | .pure-u-1-2, 444 | .pure-u-1-4, 445 | .pure-u-2-4, 446 | .pure-u-3-4, 447 | .pure-u-4-4 { 448 | display: inline-block; 449 | *display: inline; 450 | zoom: 1; 451 | letter-spacing: normal; 452 | word-spacing: normal; 453 | vertical-align: top; 454 | text-rendering: auto 455 | } 456 | .pure-u-1-4 { 457 | width: 25%; 458 | *width: 24.9690% 459 | } 460 | .pure-u-1-2, 461 | .pure-u-2-4 { 462 | width: 50%; 463 | *width: 49.9690% 464 | } 465 | .pure-u-3-4 { 466 | width: 75%; 467 | *width: 74.9690% 468 | } 469 | .pure-u-1, 470 | .pure-u-1-1, 471 | .pure-u-4-4 { 472 | width: 100% 473 | } 474 | @media screen and (min-width: 48em) { 475 | .pure-u-md-1, .pure-u-md-1-1, .pure-u-md-1-2, .pure-u-md-1-4, .pure-u-md-2-4, .pure-u-md-3-4, .pure-u-md-4-4 { 476 | display: inline-block; 477 | *display: inline; 478 | zoom: 1; 479 | letter-spacing: normal; 480 | word-spacing: normal; 481 | vertical-align: top; 482 | text-rendering: auto 483 | } 484 | .pure-u-md-1-4 { 485 | width: 25%; 486 | *width: 24.9690% 487 | } 488 | .pure-u-md-1-2, 489 | .pure-u-md-2-4 { 490 | width: 50%; 491 | *width: 49.9690% 492 | } 493 | .pure-u-md-3-4 { 494 | width: 75%; 495 | *width: 74.9690% 496 | } 497 | .pure-u-md-1, 498 | .pure-u-md-1-1, 499 | .pure-u-md-4-4 { 500 | width: 100% 501 | } 502 | } 503 | @media screen and (min-width: 64em) { 504 | .pure-u-lg-1, .pure-u-lg-1-1, .pure-u-lg-1-2, .pure-u-lg-1-4, .pure-u-lg-2-4, .pure-u-lg-3-4, .pure-u-lg-4-4 { 505 | display: inline-block; 506 | *display: inline; 507 | zoom: 1; 508 | letter-spacing: normal; 509 | word-spacing: normal; 510 | vertical-align: top; 511 | text-rendering: auto 512 | } 513 | .pure-u-lg-1-4 { 514 | width: 25%; 515 | *width: 24.9690% 516 | } 517 | .pure-u-lg-1-2, 518 | .pure-u-lg-2-4 { 519 | width: 50%; 520 | *width: 49.9690% 521 | } 522 | .pure-u-lg-3-4 { 523 | width: 75%; 524 | *width: 74.9690% 525 | } 526 | .pure-u-lg-1, 527 | .pure-u-lg-1-1, 528 | .pure-u-lg-4-4 { 529 | width: 100% 530 | } 531 | } 532 | .pure-button { 533 | display: inline-block; 534 | zoom: 1; 535 | line-height: normal; 536 | white-space: nowrap; 537 | vertical-align: middle; 538 | text-align: center; 539 | cursor: pointer; 540 | -webkit-user-drag: none; 541 | -webkit-user-select: none; 542 | -moz-user-select: none; 543 | -ms-user-select: none; 544 | user-select: none; 545 | -webkit-box-sizing: border-box; 546 | -moz-box-sizing: border-box; 547 | box-sizing: border-box 548 | } 549 | .pure-button::-moz-focus-inner { 550 | padding: 0; 551 | border: 0 552 | } 553 | .pure-button { 554 | font-family: inherit; 555 | font-size: 100%; 556 | padding: .5em 1em; 557 | color: #fff; 558 | color: rgba(255, 255, 255, 255); 559 | border: 1px solid #999; 560 | border: none rgba(0, 0, 0, 0); 561 | background-color: #e6e6e6; 562 | text-decoration: none; 563 | border-radius: 2px 564 | } 565 | .pure-button-hover, 566 | .pure-button:hover, 567 | .pure-button:focus { 568 | filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0); 569 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0, 0, 0, 0.05)), to(rgba(0, 0, 0, 0.10))); 570 | background-image: -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 571 | background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.05) 0, rgba(0, 0, 0, 0.10)); 572 | background-image: -o-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 573 | background-image: linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)) 574 | } 575 | .pure-button:focus { 576 | outline: 0 577 | } 578 | .pure-button-active, 579 | .pure-button:active { 580 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 0 6px rgba(0, 0, 0, 0.20) inset; 581 | border-color: #000\9 582 | } 583 | .pure-button[disabled], 584 | .pure-button-disabled, 585 | .pure-button-disabled:hover, 586 | .pure-button-disabled:focus, 587 | .pure-button-disabled:active { 588 | border: 0; 589 | background-image: none; 590 | filter: progid: DXImageTransform.Microsoft.gradient(enabled false); 591 | filter: alpha(opacity=40); 592 | -khtml-opacity: .40; 593 | -moz-opacity: .40; 594 | opacity: .40; 595 | cursor: not-allowed; 596 | box-shadow: none 597 | } 598 | .pure-button-hidden { 599 | display: none 600 | } 601 | .pure-button::-moz-focus-inner { 602 | padding: 0; 603 | border: 0 604 | } 605 | .pure-button-primary, 606 | .pure-button-selected, 607 | a.pure-button-primary, 608 | a.pure-button-selected { 609 | background-color: #0078e7; 610 | color: #fff 611 | } 612 | .pure-form input[type="text"], 613 | .pure-form input[type="password"], 614 | .pure-form input[type="email"], 615 | .pure-form input[type="url"], 616 | .pure-form input[type="date"], 617 | .pure-form input[type="month"], 618 | .pure-form input[type="time"], 619 | .pure-form input[type="datetime"], 620 | .pure-form input[type="datetime-local"], 621 | .pure-form input[type="week"], 622 | .pure-form input[type="number"], 623 | .pure-form input[type="search"], 624 | .pure-form input[type="tel"], 625 | .pure-form input[type="color"], 626 | .pure-form select, 627 | .pure-form textarea { 628 | padding: .5em .6em; 629 | display: inline-block; 630 | border: 1px solid #ccc; 631 | box-shadow: inset 0 1px 3px #ddd; 632 | border-radius: 4px; 633 | vertical-align: middle; 634 | -webkit-box-sizing: border-box; 635 | -moz-box-sizing: border-box; 636 | box-sizing: border-box 637 | } 638 | .pure-form input:not([type]) { 639 | padding: .5em .6em; 640 | display: inline-block; 641 | border: 1px solid #ccc; 642 | box-shadow: inset 0 1px 3px #ddd; 643 | border-radius: 4px; 644 | -webkit-box-sizing: border-box; 645 | -moz-box-sizing: border-box; 646 | box-sizing: border-box 647 | } 648 | .pure-form input[type="color"] { 649 | padding: .2em .5em 650 | } 651 | .pure-form input[type="text"]:focus, 652 | .pure-form input[type="password"]:focus, 653 | .pure-form input[type="email"]:focus, 654 | .pure-form input[type="url"]:focus, 655 | .pure-form input[type="date"]:focus, 656 | .pure-form input[type="month"]:focus, 657 | .pure-form input[type="time"]:focus, 658 | .pure-form input[type="datetime"]:focus, 659 | .pure-form input[type="datetime-local"]:focus, 660 | .pure-form input[type="week"]:focus, 661 | .pure-form input[type="number"]:focus, 662 | .pure-form input[type="search"]:focus, 663 | .pure-form input[type="tel"]:focus, 664 | .pure-form input[type="color"]:focus, 665 | .pure-form select:focus, 666 | .pure-form textarea:focus { 667 | outline: 0; 668 | border-color: #129fea 669 | } 670 | .pure-form input:not([type]):focus { 671 | outline: 0; 672 | border-color: #129fea 673 | } 674 | .pure-form input[type="file"]:focus, 675 | .pure-form input[type="radio"]:focus, 676 | .pure-form input[type="checkbox"]:focus { 677 | outline: thin solid #129fea; 678 | outline: 1px auto #129fea 679 | } 680 | .pure-form .pure-checkbox, 681 | .pure-form .pure-radio { 682 | margin: .5em 0; 683 | display: block 684 | } 685 | .pure-form input[type="text"][disabled], 686 | .pure-form input[type="password"][disabled], 687 | .pure-form input[type="email"][disabled], 688 | .pure-form input[type="url"][disabled], 689 | .pure-form input[type="date"][disabled], 690 | .pure-form input[type="month"][disabled], 691 | .pure-form input[type="time"][disabled], 692 | .pure-form input[type="datetime"][disabled], 693 | .pure-form input[type="datetime-local"][disabled], 694 | .pure-form input[type="week"][disabled], 695 | .pure-form input[type="number"][disabled], 696 | .pure-form input[type="search"][disabled], 697 | .pure-form input[type="tel"][disabled], 698 | .pure-form input[type="color"][disabled], 699 | .pure-form select[disabled], 700 | .pure-form textarea[disabled] { 701 | cursor: not-allowed; 702 | background-color: #eaeded; 703 | color: #cad2d3 704 | } 705 | .pure-form input:not([type])[disabled] { 706 | cursor: not-allowed; 707 | background-color: #eaeded; 708 | color: #cad2d3 709 | } 710 | .pure-form input[readonly], 711 | .pure-form select[readonly], 712 | .pure-form textarea[readonly] { 713 | background-color: #eee; 714 | color: #777; 715 | border-color: #ccc 716 | } 717 | .pure-form input:focus:invalid, 718 | .pure-form textarea:focus:invalid, 719 | .pure-form select:focus:invalid { 720 | color: #b94a48; 721 | border-color: #e9322d 722 | } 723 | .pure-form input[type="file"]:focus:invalid:focus, 724 | .pure-form input[type="radio"]:focus:invalid:focus, 725 | .pure-form input[type="checkbox"]:focus:invalid:focus { 726 | outline-color: #e9322d 727 | } 728 | .pure-form select { 729 | height: 2.25em; 730 | border: 1px solid #ccc; 731 | background-color: white 732 | } 733 | .pure-form select[multiple] { 734 | height: auto 735 | } 736 | .pure-form label { 737 | margin: .5em 0 .2em 738 | } 739 | .pure-form fieldset { 740 | margin: 0; 741 | padding: .35em 0 .75em; 742 | border: 0 743 | } 744 | .pure-form legend { 745 | display: block; 746 | width: 100%; 747 | padding: .3em 0; 748 | margin-bottom: .3em; 749 | color: #333; 750 | border-bottom: 1px solid #e5e5e5 751 | } 752 | .pure-form-stacked input[type="text"], 753 | .pure-form-stacked input[type="password"], 754 | .pure-form-stacked input[type="email"], 755 | .pure-form-stacked input[type="url"], 756 | .pure-form-stacked input[type="date"], 757 | .pure-form-stacked input[type="month"], 758 | .pure-form-stacked input[type="time"], 759 | .pure-form-stacked input[type="datetime"], 760 | .pure-form-stacked input[type="datetime-local"], 761 | .pure-form-stacked input[type="week"], 762 | .pure-form-stacked input[type="number"], 763 | .pure-form-stacked input[type="search"], 764 | .pure-form-stacked input[type="tel"], 765 | .pure-form-stacked input[type="color"], 766 | .pure-form-stacked input[type="file"], 767 | .pure-form-stacked select, 768 | .pure-form-stacked label, 769 | .pure-form-stacked textarea { 770 | display: block; 771 | margin: .25em 0 772 | } 773 | .pure-form-stacked input:not([type]) { 774 | display: block; 775 | margin: .25em 0 776 | } 777 | .pure-form-aligned input, 778 | .pure-form-aligned textarea, 779 | .pure-form-aligned select, 780 | .pure-form-aligned .pure-help-inline, 781 | .pure-form-message-inline { 782 | display: inline-block; 783 | *display: inline; 784 | *zoom: 1; 785 | vertical-align: middle 786 | } 787 | .pure-form-aligned textarea { 788 | vertical-align: top 789 | } 790 | .pure-form-aligned .pure-control-group { 791 | margin-bottom: .5em 792 | } 793 | .pure-form-aligned .pure-control-group label { 794 | text-align: right; 795 | display: inline-block; 796 | vertical-align: middle; 797 | width: 10em; 798 | margin: 0 1em 0 0 799 | } 800 | .pure-form-aligned .pure-controls { 801 | margin: 1.5em 0 0 11em 802 | } 803 | .pure-form input.pure-input-rounded, 804 | .pure-form .pure-input-rounded { 805 | border-radius: 2em; 806 | padding: .5em 1em 807 | } 808 | .pure-form .pure-group fieldset { 809 | margin-bottom: 10px 810 | } 811 | .pure-form .pure-group input, 812 | .pure-form .pure-group textarea { 813 | display: block; 814 | padding: 10px; 815 | margin: 0 0 -1px; 816 | border-radius: 0; 817 | position: relative; 818 | top: -1px 819 | } 820 | .pure-form .pure-group input:focus, 821 | .pure-form .pure-group textarea:focus { 822 | z-index: 3 823 | } 824 | .pure-form .pure-group input:first-child, 825 | .pure-form .pure-group textarea:first-child { 826 | top: 1px; 827 | border-radius: 4px 4px 0 0; 828 | margin: 0 829 | } 830 | .pure-form .pure-group input:first-child:last-child, 831 | .pure-form .pure-group textarea:first-child:last-child { 832 | top: 1px; 833 | border-radius: 4px; 834 | margin: 0 835 | } 836 | .pure-form .pure-group input:last-child, 837 | .pure-form .pure-group textarea:last-child { 838 | top: -2px; 839 | border-radius: 0 0 4px 4px; 840 | margin: 0 841 | } 842 | .pure-form .pure-group button { 843 | margin: .35em 0 844 | } 845 | .pure-form .pure-input-1 { 846 | width: 100% 847 | } 848 | .pure-form .pure-input-2-3 { 849 | width: 66% 850 | } 851 | .pure-form .pure-input-1-2 { 852 | width: 50% 853 | } 854 | .pure-form .pure-input-1-3 { 855 | width: 33% 856 | } 857 | .pure-form .pure-input-1-4 { 858 | width: 25% 859 | } 860 | .pure-form .pure-help-inline, 861 | .pure-form-message-inline { 862 | display: inline-block; 863 | padding-left: .3em; 864 | color: #666; 865 | vertical-align: middle; 866 | font-size: .875em 867 | } 868 | .pure-form-message { 869 | display: block; 870 | color: #666; 871 | font-size: .875em 872 | } 873 | @media only screen and (max-width: 480px) { 874 | .pure-form button[type="submit"] { 875 | margin: .7em 0 0 876 | } 877 | .pure-form input:not([type]), 878 | .pure-form input[type="text"], 879 | .pure-form input[type="password"], 880 | .pure-form input[type="email"], 881 | .pure-form input[type="url"], 882 | .pure-form input[type="date"], 883 | .pure-form input[type="month"], 884 | .pure-form input[type="time"], 885 | .pure-form input[type="datetime"], 886 | .pure-form input[type="datetime-local"], 887 | .pure-form input[type="week"], 888 | .pure-form input[type="number"], 889 | .pure-form input[type="search"], 890 | .pure-form input[type="tel"], 891 | .pure-form input[type="color"], 892 | .pure-form label { 893 | margin-bottom: .3em; 894 | display: block 895 | } 896 | .pure-group input:not([type]), 897 | .pure-group input[type="text"], 898 | .pure-group input[type="password"], 899 | .pure-group input[type="email"], 900 | .pure-group input[type="url"], 901 | .pure-group input[type="date"], 902 | .pure-group input[type="month"], 903 | .pure-group input[type="time"], 904 | .pure-group input[type="datetime"], 905 | .pure-group input[type="datetime-local"], 906 | .pure-group input[type="week"], 907 | .pure-group input[type="number"], 908 | .pure-group input[type="search"], 909 | .pure-group input[type="tel"], 910 | .pure-group input[type="color"] { 911 | margin-bottom: 0 912 | } 913 | .pure-form-aligned .pure-control-group label { 914 | margin-bottom: .3em; 915 | text-align: left; 916 | display: block; 917 | width: 100% 918 | } 919 | .pure-form-aligned .pure-controls { 920 | margin: 1.5em 0 0 0 921 | } 922 | .pure-form .pure-help-inline, 923 | .pure-form-message-inline, 924 | .pure-form-message { 925 | display: block; 926 | font-size: .75em; 927 | padding: .2em 0 .8em 928 | } 929 | } 930 | .pure-menu { 931 | -webkit-box-sizing: border-box; 932 | -moz-box-sizing: border-box; 933 | box-sizing: border-box 934 | } 935 | .pure-menu-fixed { 936 | position: fixed; 937 | left: 0; 938 | top: 0; 939 | z-index: 3 940 | } 941 | .pure-menu-list, 942 | .pure-menu-item { 943 | position: relative 944 | } 945 | .pure-menu-list { 946 | list-style: none; 947 | margin: 0; 948 | padding: 0 949 | } 950 | .pure-menu-item { 951 | padding: 0; 952 | margin: 0; 953 | height: 100% 954 | } 955 | .pure-menu-link, 956 | .pure-menu-heading { 957 | display: block; 958 | text-decoration: none; 959 | white-space: nowrap 960 | } 961 | .pure-menu-horizontal { 962 | width: 100%; 963 | white-space: nowrap 964 | } 965 | .pure-menu-horizontal .pure-menu-list { 966 | display: inline-block 967 | } 968 | .pure-menu-horizontal .pure-menu-item, 969 | .pure-menu-horizontal .pure-menu-heading, 970 | .pure-menu-horizontal .pure-menu-separator { 971 | display: inline-block; 972 | *display: inline; 973 | zoom: 1; 974 | vertical-align: middle 975 | } 976 | .pure-menu-item .pure-menu-item { 977 | display: block 978 | } 979 | .pure-menu-children { 980 | display: none; 981 | position: absolute; 982 | left: 100%; 983 | top: 0; 984 | margin: 0; 985 | padding: 0; 986 | z-index: 3 987 | } 988 | .pure-menu-horizontal .pure-menu-children { 989 | left: 0; 990 | top: auto; 991 | width: inherit 992 | } 993 | .pure-menu-allow-hover:hover>.pure-menu-children, 994 | .pure-menu-active>.pure-menu-children { 995 | display: block; 996 | position: absolute 997 | } 998 | .pure-menu-has-children>.pure-menu-link:after { 999 | padding-left: .5em; 1000 | content: "\25B8"; 1001 | font-size: small 1002 | } 1003 | .pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after { 1004 | content: "\25BE" 1005 | } 1006 | .pure-menu-scrollable { 1007 | overflow-y: scroll; 1008 | overflow-x: hidden 1009 | } 1010 | .pure-menu-scrollable .pure-menu-list { 1011 | display: block 1012 | } 1013 | .pure-menu-horizontal.pure-menu-scrollable .pure-menu-list { 1014 | display: inline-block 1015 | } 1016 | .pure-menu-horizontal.pure-menu-scrollable { 1017 | white-space: nowrap; 1018 | overflow-y: hidden; 1019 | overflow-x: auto; 1020 | -ms-overflow-style: none; 1021 | -webkit-overflow-scrolling: touch; 1022 | padding: .5em 0 1023 | } 1024 | .pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar { 1025 | display: none 1026 | } 1027 | .pure-menu-separator { 1028 | background-color: #ccc; 1029 | height: 1px; 1030 | margin: .3em 0 1031 | } 1032 | .pure-menu-horizontal .pure-menu-separator { 1033 | width: 1px; 1034 | height: 1.3em; 1035 | margin: 0 .3em 1036 | } 1037 | .pure-menu-heading { 1038 | text-transform: uppercase; 1039 | color: #565d64 1040 | } 1041 | .pure-menu-link { 1042 | color: #777 1043 | } 1044 | .pure-menu-children { 1045 | background-color: #fff 1046 | } 1047 | .pure-menu-link, 1048 | .pure-menu-disabled, 1049 | .pure-menu-heading { 1050 | padding: .5em 1em 1051 | } 1052 | .pure-menu-disabled { 1053 | opacity: .5 1054 | } 1055 | .pure-menu-disabled .pure-menu-link:hover { 1056 | background-color: transparent 1057 | } 1058 | .pure-menu-active>.pure-menu-link, 1059 | .pure-menu-link:hover, 1060 | .pure-menu-link:focus { 1061 | background-color: #00979c; 1062 | color: #00999a 1063 | } 1064 | .pure-menu-selected .pure-menu-link, 1065 | .pure-menu-selected .pure-menu-link:visited { 1066 | color: #fff 1067 | } 1068 | .pure-table { 1069 | border-collapse: collapse; 1070 | border-spacing: 0; 1071 | empty-cells: show 1072 | } 1073 | .pure-table caption { 1074 | color: #000; 1075 | font: italic 85%/1 arial, sans-serif; 1076 | padding: 1em 0; 1077 | text-align: center 1078 | } 1079 | .pure-table td, 1080 | .pure-table th { 1081 | border-left: 1px solid #cbcbcb; 1082 | border-width: 0 0 0 1px; 1083 | font-size: inherit; 1084 | margin: 0; 1085 | overflow: visible; 1086 | padding: .5em 1em; 1087 | color: #464646 !important 1088 | } 1089 | .pure-table td:first-child, 1090 | .pure-table th:first-child { 1091 | border-left-width: 0; 1092 | color: #777 !important 1093 | } 1094 | .pure-table thead { 1095 | background-color: #e0e0e0; 1096 | color: #000; 1097 | text-align: left; 1098 | vertical-align: bottom 1099 | } 1100 | .pure-table td { 1101 | background-color: transparent 1102 | } 1103 | .pure-table-odd td { 1104 | background-color: #f2f2f2 1105 | } 1106 | .pure-table-striped tr:nth-child(2n-1) td { 1107 | background-color: #f2f2f2 1108 | } 1109 | .pure-table-bordered td { 1110 | border-bottom: 1px solid #cbcbcb 1111 | } 1112 | .pure-table-bordered tbody>tr:last-child>td { 1113 | border-bottom-width: 0 1114 | } 1115 | .pure-table-horizontal td, 1116 | .pure-table-horizontal th { 1117 | border-width: 0 0 1px 0; 1118 | border-bottom: 1px solid #cbcbcb 1119 | } 1120 | .pure-table-horizontal tbody>tr:last-child>td { 1121 | border-bottom-width: 0 1122 | } 1123 | p; 1124 | display:-ms-flexbox; 1125 | -ms-flex-flow:row wrap; 1126 | -ms-align-content:flex-start; 1127 | -webkit-align-content:flex-start; 1128 | align-content:flex-start 1129 | } 1130 | .opera-only:-o-prefocus, 1131 | .pure-g { 1132 | word-spacing: -0.43em 1133 | } 1134 | .pure-u { 1135 | display: inline-block; 1136 | *display: inline; 1137 | zoom: 1; 1138 | letter-spacing: normal; 1139 | word-spacing: normal; 1140 | vertical-align: top; 1141 | text-rendering: auto 1142 | } 1143 | .pure-g [class * "pure-u"] { 1144 | font-family: sans-serif 1145 | } 1146 | .pure-u-1, 1147 | .pure-u-1-1, 1148 | .pure-u-1-2, 1149 | .pure-u-1-4, 1150 | .pure-u-2-4, 1151 | .pure-u-3-4, 1152 | .pure-u-4-4 { 1153 | display: inline-block; 1154 | *display: inline; 1155 | zoom: 1; 1156 | letter-spacing: normal; 1157 | word-spacing: normal; 1158 | vertical-align: top; 1159 | text-rendering: auto 1160 | } 1161 | .pure-u-1-4 { 1162 | width: 25%; 1163 | *width: 24.9690% 1164 | } 1165 | .pure-u-1-2, 1166 | .pure-u-2-4 { 1167 | width: 50%; 1168 | *width: 49.9690% 1169 | } 1170 | .pure-u-3-4 { 1171 | width: 75%; 1172 | *width: 74.9690% 1173 | } 1174 | .pure-u-1, 1175 | .pure-u-1-1, 1176 | .pure-u-4-4 { 1177 | width: 100% 1178 | } 1179 | @media screen and (min-width: 48em) { 1180 | .pure-u-md-1, 1181 | .pure-u-md-1-1, 1182 | .pure-u-md-1-2, 1183 | .pure-u-md-1-4, 1184 | .pure-u-md-2-4, 1185 | .pure-u-md-3-4, 1186 | .pure-u-md-4-4 { 1187 | display: inline-block; 1188 | *display: inline; 1189 | zoom: 1; 1190 | letter-spacing: normal; 1191 | word-spacing: normal; 1192 | vertical-align: top; 1193 | text-rendering: auto 1194 | } 1195 | .pure-u-md-1-4 { 1196 | width: 25%; 1197 | *width: 24.9690% 1198 | } 1199 | .pure-u-md-1-2, 1200 | .pure-u-md-2-4 { 1201 | width: 50%; 1202 | *width: 49.9690% 1203 | } 1204 | .pure-u-md-3-4 { 1205 | width: 75%; 1206 | *width: 74.9690% 1207 | } 1208 | .pure-u-md-1, 1209 | .pure-u-md-1-1, 1210 | .pure-u-md-4-4 { 1211 | width: 100% 1212 | } 1213 | } 1214 | @media screen and (min-width: 64em) { 1215 | .pure-u-lg-1, 1216 | .pure-u-lg-1-1, 1217 | .pure-u-lg-1-2, 1218 | .pure-u-lg-1-4, 1219 | .pure-u-lg-2-4, 1220 | .pure-u-lg-3-4, 1221 | .pure-u-lg-4-4 { 1222 | display: inline-block; 1223 | *display: inline; 1224 | zoom: 1; 1225 | letter-spacing: normal; 1226 | word-spacing: normal; 1227 | vertical-align: top; 1228 | text-rendering: auto 1229 | } 1230 | .pure-u-lg-1-4 { 1231 | width: 25%; 1232 | *width: 24.9690% 1233 | } 1234 | .pure-u-lg-1-2, 1235 | .pure-u-lg-2-4 { 1236 | width: 50%; 1237 | *width: 49.9690% 1238 | } 1239 | .pure-u-lg-3-4 { 1240 | width: 75%; 1241 | *width: 74.9690% 1242 | } 1243 | .pure-u-lg-1, 1244 | .pure-u-lg-1-1, 1245 | .pure-u-lg-4-4 { 1246 | width: 100% 1247 | } 1248 | } 1249 | .pure-button { 1250 | display: inline-block; 1251 | zoom: 1; 1252 | line-height: normal; 1253 | white-space: nowrap; 1254 | vertical-align: middle; 1255 | text-align: center; 1256 | cursor: pointer; 1257 | -webkit-user-drag: none; 1258 | -webkit-user-select: none; 1259 | -moz-user-select: none; 1260 | -ms-user-select: none; 1261 | user-select: none; 1262 | -webkit-box-sizing: border-box; 1263 | -moz-box-sizing: border-box; 1264 | box-sizing: border-box 1265 | } 1266 | .pure-button::-moz-focus-inner { 1267 | padding: 0; 1268 | border: 0 1269 | } 1270 | .pure-button { 1271 | font-family: inherit; 1272 | font-size: 100%; 1273 | padding: .5em 1em; 1274 | color: #fff; 1275 | color: rgba(255, 255, 255, 255); 1276 | border: 1px solid #999; 1277 | border: none rgba(0, 0, 0, 0); 1278 | background-color: #e6e6e6; 1279 | text-decoration: none; 1280 | border-radius: 2px 1281 | } 1282 | .pure-button-hover, 1283 | .pure-button:hover, 1284 | .pure-button:focus { 1285 | filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0); 1286 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0, 0, 0, 0.05)), to(rgba(0, 0, 0, 0.10))); 1287 | background-image: -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 1288 | background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.05) 0, rgba(0, 0, 0, 0.10)); 1289 | background-image: -o-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 1290 | background-image: linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)) 1291 | } 1292 | .pure-button:focus { 1293 | outline: 0 1294 | } 1295 | .pure-button-active, 1296 | .pure-button:active { 1297 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 0 6px rgba(0, 0, 0, 0.20) inset; 1298 | border-color: #000\9 1299 | } 1300 | .pure-button[disabled], 1301 | .pure-button-disabled, 1302 | .pure-button-disabled:hover, 1303 | .pure-button-disabled:focus, 1304 | .pure-button-disabled:active { 1305 | border: 0; 1306 | background-image: none; 1307 | filter: progid: DXImageTransform.Microsoft.gradient(enabled false); 1308 | filter: alpha(opacity=40); 1309 | -khtml-opacity: .40; 1310 | -moz-opacity: .40; 1311 | opacity: .40; 1312 | cursor: not-allowed; 1313 | box-shadow: none 1314 | } 1315 | .pure-button-hidden { 1316 | display: none 1317 | } 1318 | .pure-button::-moz-focus-inner { 1319 | padding: 0; 1320 | border: 0 1321 | } 1322 | .pure-button-primary, 1323 | .pure-button-selected, 1324 | a.pure-button-primary, 1325 | a.pure-button-selected { 1326 | background-color: #0078e7; 1327 | color: #fff 1328 | } 1329 | .pure-form input[type="text"], 1330 | .pure-form input[type="password"], 1331 | .pure-form input[type="email"], 1332 | .pure-form input[type="url"], 1333 | .pure-form input[type="date"], 1334 | .pure-form input[type="month"], 1335 | .pure-form input[type="time"], 1336 | .pure-form input[type="datetime"], 1337 | .pure-form input[type="datetime-local"], 1338 | .pure-form input[type="week"], 1339 | .pure-form input[type="number"], 1340 | .pure-form input[type="search"], 1341 | .pure-form input[type="tel"], 1342 | .pure-form input[type="color"], 1343 | .pure-form select, 1344 | .pure-form textarea { 1345 | padding: .5em .6em; 1346 | display: inline-block; 1347 | border: 1px solid #ccc; 1348 | box-shadow: inset 0 1px 3px #ddd; 1349 | border-radius: 4px; 1350 | vertical-align: middle; 1351 | -webkit-box-sizing: border-box; 1352 | -moz-box-sizing: border-box; 1353 | box-sizing: border-box 1354 | } 1355 | .pure-form input:not([type]) { 1356 | padding: .5em .6em; 1357 | display: inline-block; 1358 | border: 1px solid #ccc; 1359 | box-shadow: inset 0 1px 3px #ddd; 1360 | border-radius: 4px; 1361 | -webkit-box-sizing: border-box; 1362 | -moz-box-sizing: border-box; 1363 | box-sizing: border-box 1364 | } 1365 | .pure-form input[type="color"] { 1366 | padding: .2em .5em 1367 | } 1368 | .pure-form input[type="text"]:focus, 1369 | .pure-form input[type="password"]:focus, 1370 | .pure-form input[type="email"]:focus, 1371 | .pure-form input[type="url"]:focus, 1372 | .pure-form input[type="date"]:focus, 1373 | .pure-form input[type="month"]:focus, 1374 | .pure-form input[type="time"]:focus, 1375 | .pure-form input[type="datetime"]:focus, 1376 | .pure-form input[type="datetime-local"]:focus, 1377 | .pure-form input[type="week"]:focus, 1378 | .pure-form input[type="number"]:focus, 1379 | .pure-form input[type="search"]:focus, 1380 | .pure-form input[type="tel"]:focus, 1381 | .pure-form input[type="color"]:focus, 1382 | .pure-form select:focus, 1383 | .pure-form textarea:focus { 1384 | outline: 0; 1385 | border-color: #129fea 1386 | } 1387 | .pure-form input:not([type]):focus { 1388 | outline: 0; 1389 | border-color: #129fea 1390 | } 1391 | .pure-form input[type="file"]:focus, 1392 | .pure-form input[type="radio"]:focus, 1393 | .pure-form input[type="checkbox"]:focus { 1394 | outline: thin solid #129fea; 1395 | outline: 1px auto #129fea 1396 | } 1397 | .pure-form .pure-checkbox, 1398 | .pure-form .pure-radio { 1399 | margin: .5em 0; 1400 | display: block 1401 | } 1402 | .pure-form input[type="text"][disabled], 1403 | .pure-form input[type="password"][disabled], 1404 | .pure-form input[type="email"][disabled], 1405 | .pure-form input[type="url"][disabled], 1406 | .pure-form input[type="date"][disabled], 1407 | .pure-form input[type="month"][disabled], 1408 | .pure-form input[type="time"][disabled], 1409 | .pure-form input[type="datetime"][disabled], 1410 | .pure-form input[type="datetime-local"][disabled], 1411 | .pure-form input[type="week"][disabled], 1412 | .pure-form input[type="number"][disabled], 1413 | .pure-form input[type="search"][disabled], 1414 | .pure-form input[type="tel"][disabled], 1415 | .pure-form input[type="color"][disabled], 1416 | .pure-form select[disabled], 1417 | .pure-form textarea[disabled] { 1418 | cursor: not-allowed; 1419 | background-color: #eaeded; 1420 | color: #cad2d3 1421 | } 1422 | .pure-form input:not([type])[disabled] { 1423 | cursor: not-allowed; 1424 | background-color: #eaeded; 1425 | color: #cad2d3 1426 | } 1427 | .pure-form input[readonly], 1428 | .pure-form select[readonly], 1429 | .pure-form textarea[readonly] { 1430 | background-color: #eee; 1431 | color: #777; 1432 | border-color: #ccc 1433 | } 1434 | .pure-form input:focus:invalid, 1435 | .pure-form textarea:focus:invalid, 1436 | .pure-form select:focus:invalid { 1437 | color: #b94a48; 1438 | border-color: #e9322d 1439 | } 1440 | .pure-form input[type="file"]:focus:invalid:focus, 1441 | .pure-form input[type="radio"]:focus:invalid:focus, 1442 | .pure-form input[type="checkbox"]:focus:invalid:focus { 1443 | outline-color: #e9322d 1444 | } 1445 | .pure-form select { 1446 | height: 2.25em; 1447 | border: 1px solid #ccc; 1448 | background-color: white 1449 | } 1450 | .pure-form select[multiple] { 1451 | height: auto 1452 | } 1453 | .pure-form label { 1454 | margin: .5em 0 .2em 1455 | } 1456 | .pure-form fieldset { 1457 | margin: 0; 1458 | padding: .35em 0 .75em; 1459 | border: 0 1460 | } 1461 | .pure-form legend { 1462 | display: block; 1463 | width: 100%; 1464 | padding: .3em 0; 1465 | margin-bottom: .3em; 1466 | color: #333; 1467 | border-bottom: 1px solid #e5e5e5 1468 | } 1469 | .pure-form-stacked input[type="text"], 1470 | .pure-form-stacked input[type="password"], 1471 | .pure-form-stacked input[type="email"], 1472 | .pure-form-stacked input[type="url"], 1473 | .pure-form-stacked input[type="date"], 1474 | .pure-form-stacked input[type="month"], 1475 | .pure-form-stacked input[type="time"], 1476 | .pure-form-stacked input[type="datetime"], 1477 | .pure-form-stacked input[type="datetime-local"], 1478 | .pure-form-stacked input[type="week"], 1479 | .pure-form-stacked input[type="number"], 1480 | .pure-form-stacked input[type="search"], 1481 | .pure-form-stacked input[type="tel"], 1482 | .pure-form-stacked input[type="color"], 1483 | .pure-form-stacked input[type="file"], 1484 | .pure-form-stacked select, 1485 | .pure-form-stacked label, 1486 | .pure-form-stacked textarea { 1487 | display: block; 1488 | margin: .25em 0 1489 | } 1490 | .pure-form-stacked input:not([type]) { 1491 | display: block; 1492 | margin: .25em 0 1493 | } 1494 | .pure-form-aligned input, 1495 | .pure-form-aligned textarea, 1496 | .pure-form-aligned select, 1497 | .pure-form-aligned .pure-help-inline, 1498 | .pure-form-message-inline { 1499 | display: inline-block; 1500 | *display: inline; 1501 | *zoom: 1; 1502 | vertical-align: middle 1503 | } 1504 | .pure-form-aligned textarea { 1505 | vertical-align: top 1506 | } 1507 | .pure-form-aligned .pure-control-group { 1508 | margin-bottom: .5em 1509 | } 1510 | .pure-form-aligned .pure-control-group label { 1511 | text-align: right; 1512 | display: inline-block; 1513 | vertical-align: middle; 1514 | width: 10em; 1515 | margin: 0 1em 0 0 1516 | } 1517 | .pure-form-aligned .pure-controls { 1518 | margin: 1.5em 0 0 11em 1519 | } 1520 | .pure-form input.pure-input-rounded, 1521 | .pure-form .pure-input-rounded { 1522 | border-radius: 2em; 1523 | padding: .5em 1em 1524 | } 1525 | .pure-form .pure-group fieldset { 1526 | margin-bottom: 10px 1527 | } 1528 | .pure-form .pure-group input, 1529 | .pure-form .pure-group textarea { 1530 | display: block; 1531 | padding: 10px; 1532 | margin: 0 0 -1px; 1533 | border-radius: 0; 1534 | position: relative; 1535 | top: -1px 1536 | } 1537 | .pure-form .pure-group input:focus, 1538 | .pure-form .pure-group textarea:focus { 1539 | z-index: 3 1540 | } 1541 | .pure-form .pure-group input:first-child, 1542 | .pure-form .pure-group textarea:first-child { 1543 | top: 1px; 1544 | border-radius: 4px 4px 0 0; 1545 | margin: 0 1546 | } 1547 | .pure-form .pure-group input:first-child:last-child, 1548 | .pure-form .pure-group textarea:first-child:last-child { 1549 | top: 1px; 1550 | border-radius: 4px; 1551 | margin: 0 1552 | } 1553 | .pure-form .pure-group input:last-child, 1554 | .pure-form .pure-group textarea:last-child { 1555 | top: -2px; 1556 | border-radius: 0 0 4px 4px; 1557 | margin: 0 1558 | } 1559 | .pure-form .pure-group button { 1560 | margin: .35em 0 1561 | } 1562 | .pure-form .pure-input-1 { 1563 | width: 100% 1564 | } 1565 | .pure-form .pure-input-2-3 { 1566 | width: 66% 1567 | } 1568 | .pure-form .pure-input-1-2 { 1569 | width: 50% 1570 | } 1571 | .pure-form .pure-input-1-3 { 1572 | width: 33% 1573 | } 1574 | .pure-form .pure-input-1-4 { 1575 | width: 25% 1576 | } 1577 | .pure-form .pure-help-inline, 1578 | .pure-form-message-inline { 1579 | display: inline-block; 1580 | padding-left: .3em; 1581 | color: #666; 1582 | vertical-align: middle; 1583 | font-size: .875em 1584 | } 1585 | .pure-form-message { 1586 | display: block; 1587 | color: #666; 1588 | font-size: .875em 1589 | } 1590 | @media only screen and (max-width: 480px) { 1591 | .pure-form button[type="submit"] { 1592 | margin: .7em 0 0 1593 | } 1594 | .pure-form input:not([type]), 1595 | .pure-form input[type="text"], 1596 | .pure-form input[type="password"], 1597 | .pure-form input[type="email"], 1598 | .pure-form input[type="url"], 1599 | .pure-form input[type="date"], 1600 | .pure-form input[type="month"], 1601 | .pure-form input[type="time"], 1602 | .pure-form input[type="datetime"], 1603 | .pure-form input[type="datetime-local"], 1604 | .pure-form input[type="week"], 1605 | .pure-form input[type="number"], 1606 | .pure-form input[type="search"], 1607 | .pure-form input[type="tel"], 1608 | .pure-form input[type="color"], 1609 | .pure-form label { 1610 | margin-bottom: .3em; 1611 | display: block 1612 | } 1613 | .pure-group input:not([type]), 1614 | .pure-group input[type="text"], 1615 | .pure-group input[type="password"], 1616 | .pure-group input[type="email"], 1617 | .pure-group input[type="url"], 1618 | .pure-group input[type="date"], 1619 | .pure-group input[type="month"], 1620 | .pure-group input[type="time"], 1621 | .pure-group input[type="datetime"], 1622 | .pure-group input[type="datetime-local"], 1623 | .pure-group input[type="week"], 1624 | .pure-group input[type="number"], 1625 | .pure-group input[type="search"], 1626 | .pure-group input[type="tel"], 1627 | .pure-group input[type="color"] { 1628 | margin-bottom: 0 1629 | } 1630 | .pure-form-aligned .pure-control-group label { 1631 | margin-bottom: .3em; 1632 | text-align: left; 1633 | display: block; 1634 | width: 100% 1635 | } 1636 | .pure-form-aligned .pure-controls { 1637 | margin: 1.5em 0 0 0 1638 | } 1639 | .pure-form .pure-help-inline, 1640 | .pure-form-message-inline, 1641 | .pure-form-message { 1642 | display: block; 1643 | font-size: .75em; 1644 | padding: .2em 0 .8em 1645 | } 1646 | } 1647 | .pure-menu { 1648 | -webkit-box-sizing: border-box; 1649 | -moz-box-sizing: border-box; 1650 | box-sizing: border-box 1651 | } 1652 | .pure-menu-fixed { 1653 | position: fixed; 1654 | left: 0; 1655 | top: 0; 1656 | z-index: 3 1657 | } 1658 | .pure-menu-list, 1659 | .pure-menu-item { 1660 | position: relative 1661 | } 1662 | .pure-menu-list { 1663 | list-style: none; 1664 | margin: 0; 1665 | padding: 0 1666 | } 1667 | .pure-menu-item { 1668 | padding: 0; 1669 | margin: 0; 1670 | height: 100% 1671 | } 1672 | .pure-menu-link, 1673 | .pure-menu-heading { 1674 | display: block; 1675 | text-decoration: none; 1676 | white-space: nowrap 1677 | } 1678 | .pure-menu-horizontal { 1679 | width: 100%; 1680 | white-space: nowrap 1681 | } 1682 | .pure-menu-horizontal .pure-menu-list { 1683 | display: inline-block 1684 | } 1685 | .pure-menu-horizontal .pure-menu-item, 1686 | .pure-menu-horizontal .pure-menu-heading, 1687 | .pure-menu-horizontal .pure-menu-separator { 1688 | display: inline-block; 1689 | *display: inline; 1690 | zoom: 1; 1691 | vertical-align: middle 1692 | } 1693 | .pure-menu-item .pure-menu-item { 1694 | display: block 1695 | } 1696 | .pure-menu-children { 1697 | display: none; 1698 | position: absolute; 1699 | left: 100%; 1700 | top: 0; 1701 | margin: 0; 1702 | padding: 0; 1703 | z-index: 3 1704 | } 1705 | .pure-menu-horizontal .pure-menu-children { 1706 | left: 0; 1707 | top: auto; 1708 | width: inherit 1709 | } 1710 | .pure-menu-allow-hover:hover>.pure-menu-children, 1711 | .pure-menu-active>.pure-menu-children { 1712 | display: block; 1713 | position: absolute 1714 | } 1715 | .pure-menu-has-children>.pure-menu-link:after { 1716 | padding-left: .5em; 1717 | content: "\25B8"; 1718 | font-size: small 1719 | } 1720 | .pure-menu-horizontal .pure-menu-has-children>.pure-menu-link:after { 1721 | content: "\25BE" 1722 | } 1723 | .pure-menu-scrollable { 1724 | overflow-y: scroll; 1725 | overflow-x: hidden 1726 | } 1727 | .pure-menu-scrollable .pure-menu-list { 1728 | display: block 1729 | } 1730 | .pure-menu-horizontal.pure-menu-scrollable .pure-menu-list { 1731 | display: inline-block 1732 | } 1733 | .pure-menu-horizontal.pure-menu-scrollable { 1734 | white-space: nowrap; 1735 | overflow-y: hidden; 1736 | overflow-x: auto; 1737 | -ms-overflow-style: none; 1738 | -webkit-overflow-scrolling: touch; 1739 | padding: .5em 0 1740 | } 1741 | .pure-menu-horizontal.pure-menu-scrollable::-webkit-scrollbar { 1742 | display: none 1743 | } 1744 | .pure-menu-separator { 1745 | background-color: #ccc; 1746 | height: 1px; 1747 | margin: .3em 0 1748 | } 1749 | .pure-menu-horizontal .pure-menu-separator { 1750 | width: 1px; 1751 | height: 1.3em; 1752 | margin: 0 .3em 1753 | } 1754 | .pure-menu-heading { 1755 | text-transform: uppercase; 1756 | color: #565d64 1757 | } 1758 | .pure-menu-link { 1759 | color: #777 1760 | } 1761 | .pure-menu-children { 1762 | background-color: #fff 1763 | } 1764 | .pure-menu-link, 1765 | .pure-menu-disabled, 1766 | .pure-menu-heading { 1767 | padding: .5em 1em 1768 | } 1769 | .pure-menu-disabled { 1770 | opacity: .5 1771 | } 1772 | .pure-menu-disabled .pure-menu-link:hover { 1773 | background-color: transparent 1774 | } 1775 | .pure-menu-active>.pure-menu-link, 1776 | .pure-menu-link:hover, 1777 | .pure-menu-link:focus { 1778 | background-color: #00979c; 1779 | color: #00999a 1780 | } 1781 | .pure-menu-selected .pure-menu-link, 1782 | .pure-menu-selected .pure-menu-link:visited { 1783 | color: #fff 1784 | } 1785 | .pure-table { 1786 | border-collapse: collapse; 1787 | border-spacing: 0; 1788 | empty-cells: show 1789 | } 1790 | .pure-table caption { 1791 | color: #000; 1792 | font: italic 85%/1 arial, sans-serif; 1793 | padding: 1em 0; 1794 | text-align: center 1795 | } 1796 | .pure-table td, 1797 | .pure-table th { 1798 | border-left: 1px solid #cbcbcb; 1799 | border-width: 0 0 0 1px; 1800 | font-size: inherit; 1801 | margin: 0; 1802 | overflow: visible; 1803 | padding: .5em 1em; 1804 | color: #464646 !important 1805 | } 1806 | .pure-table td:first-child, 1807 | .pure-table th:first-child { 1808 | border-left-width: 0; 1809 | color: #777 !important 1810 | } 1811 | .pure-table thead { 1812 | background-color: #e0e0e0; 1813 | color: #000; 1814 | text-align: left; 1815 | vertical-align: bottom 1816 | } 1817 | .pure-table td { 1818 | background-color: transparent 1819 | } 1820 | .pure-table-odd td { 1821 | background-color: #f2f2f2 1822 | } 1823 | .pure-table-striped tr:nth-child(2n-1) td { 1824 | background-color: #f2f2f2 1825 | } 1826 | .pure-table-bordered td { 1827 | border-bottom: 1px solid #cbcbcb 1828 | } 1829 | .pure-table-bordered tbody>tr:last-child>td { 1830 | border-bottom-width: 0 1831 | } 1832 | .pure-table-horizontal td, 1833 | .pure-table-horizontal th { 1834 | border-width: 0 0 1px 0; 1835 | border-bottom: 1px solid #cbcbcb 1836 | } 1837 | .pure-table-horizontal tbody>tr:last-child>td { 1838 | border-bottom-width: 0 1839 | } -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | button, 3 | input, 4 | select, 5 | textarea, 6 | .pure-g [class * "pure-u"] { 7 | font-family: sans-serif 8 | } 9 | input[type="text"], 10 | input[type="password"], 11 | textarea { 12 | width: 100% 13 | } 14 | input[type=checkbox] { 15 | float: left; 16 | margin: .35em .4em 17 | } 18 | body { 19 | color: #777 20 | } 21 | .card { 22 | background-color: #f5f6f6; 23 | margin: .5em; 24 | border-radius: .5em; 25 | border: 0 solid #000 26 | } 27 | .click-to-edit { 28 | position: relative 29 | } 30 | .edit-off { 31 | cursor: pointer 32 | } 33 | .click-to-edit input, 34 | .click-to-edit textarea { 35 | color: black; 36 | background-color: #fff; 37 | width: 100%; 38 | height: 3vh 39 | } 40 | .click-to-edit span, 41 | .click-to-edit div { 42 | padding: .3em; 43 | width: 100%; 44 | color: #fff; 45 | color: rgba(255, 255, 255, 255); 46 | border: 1px solid #999; 47 | border: none rgba(0, 0, 0, 0); 48 | background-color: #e6e6e6; 49 | text-decoration: none; 50 | border-radius: 2px 51 | } 52 | .click-to-edit span:hover, 53 | .click-to-edit div:hover, 54 | .click-to-edit span:focus, 55 | .click-to-edit div:focus { 56 | filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#1a000000', GradientType=0); 57 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(transparent), color-stop(40%, rgba(0, 0, 0, 0.05)), to(rgba(0, 0, 0, 0.10))); 58 | background-image: -webkit-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 59 | background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.05) 0, rgba(0, 0, 0, 0.10)); 60 | background-image: -o-linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)); 61 | background-image: linear-gradient(transparent, rgba(0, 0, 0, 0.05) 40%, rgba(0, 0, 0, 0.10)) 62 | } 63 | .click-to-edit span:focus, 64 | .click-to-edit div:focus { 65 | outline: 0 66 | } 67 | .click-to-edit span:active, 68 | .click-to-edit div:active { 69 | box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15) inset, 0 0 6px rgba(0, 0, 0, 0.20) inset; 70 | border-color: #000\9 71 | } 72 | .click-to-edit span::-moz-focus-inner, 73 | .click-to-edit div::-moz-focus-inner { 74 | padding: 0; 75 | border: 0 76 | } 77 | .popup-hidden { 78 | display: none 79 | } 80 | .popup, 81 | div.popup { 82 | position: absolute; 83 | bottom: 100%; 84 | background-color: #fff0b3; 85 | border-radius: 5px; 86 | border: 0 solid #000; 87 | color: #333; 88 | font-size: 80%; 89 | line-height: 110%; 90 | z-index: 100; 91 | padding: 5px; 92 | min-width: 20em 93 | } 94 | .popup:not(.pop-left) { 95 | left: 20px 96 | } 97 | .popup.pop-left { 98 | right: 20px 99 | } 100 | .popup-target:hover .popup { 101 | display: block 102 | } 103 | .popup-target { 104 | position: relative 105 | } 106 | #aps label div { 107 | display: inline-block; 108 | margin: 0 .2em 109 | } 110 | fieldset.radios { 111 | border: 0; 112 | padding-left: 0 113 | } 114 | fieldset fields { 115 | clear: both 116 | } 117 | #pin-mux input { 118 | display: block; 119 | margin-top: .4em; 120 | float: left 121 | } 122 | #pin-mux label { 123 | display: block; 124 | margin: 0 .2em 0 1em; 125 | width: 90% 126 | } 127 | .pure-form-aligned.form-narrow .pure-control-group label { 128 | text-align: left; 129 | width: 6em 130 | } 131 | .pure-form-aligned.form-narrow input[type=checkbox], 132 | .pure-form-aligned.form-narrow input[type=radio] { 133 | float: none 134 | } 135 | .xx-pure-img-responsive { 136 | max-width: 100%; 137 | height: auto 138 | } 139 | #layout, 140 | #menu, 141 | .menu-link { 142 | -webkit-transition: all .2s ease-out; 143 | transition: all .2s ease-out 144 | } 145 | #layout { 146 | position: relative; 147 | padding-left: 0; 148 | width: 80% 149 | } 150 | #layout.active #menu { 151 | left: 150px; 152 | width: 220px 153 | } 154 | #layout.active .menu-link { 155 | left: 150px 156 | } 157 | #layout.active #version { 158 | padding: 0 20%; 159 | } 160 | div.tt { 161 | font-family: monospace; 162 | font-size: 120%; 163 | color: #390; 164 | background-color: #ddd; 165 | padding: 2px; 166 | margin: 2px 0; 167 | line-height: 100% 168 | } 169 | .content { 170 | margin: 0 auto; 171 | padding: 0 2em; 172 | max-width: 800px; 173 | margin-bottom: 20px; 174 | line-height: 1.6em; 175 | width: 100%; 176 | box-sizing: border-box; 177 | overflow: auto 178 | } 179 | .header { 180 | margin: 0; 181 | margin-bottom: 5%; 182 | color: #333; 183 | text-align: center; 184 | padding: 2.5em 2em 0; 185 | border-bottom: 1px solid #eee; 186 | background-color: #f5f6f6; 187 | height: 100%; 188 | width: 100% 189 | } 190 | .header h1 { 191 | margin: .2em 0; 192 | font-size: 3em; 193 | font-weight: 300 194 | } 195 | .header h1 .esp { 196 | font-size: 1.25em 197 | } 198 | .jl, 199 | .header h1 { 200 | /*font: normal 800 1.5em TyponineSansMonoMedium;*/ 201 | position: relative; 202 | bottom: 19px; 203 | color: #007481; 204 | margin-left: 11%; 205 | text-transform: uppercase; 206 | text-align: left; 207 | font-size: 18.2pt 208 | } 209 | .content-subhead { 210 | margin: 50px 0 20px 0; 211 | font-weight: 300; 212 | color: #888 213 | } 214 | form button { 215 | margin-top: .5em 216 | } 217 | .button-primary { 218 | background: #007481 219 | } 220 | .button-selected { 221 | background-color: #fc6 222 | } 223 | select.pure-button { 224 | padding: .465em 1em; 225 | color: #009 226 | } 227 | .button-small { 228 | font-size: 75%; 229 | background: #ccc 230 | } 231 | input.inline { 232 | float: none; 233 | margin-right: 0; 234 | margin-left: .5em 235 | } 236 | pre.console { 237 | background-color: #000; 238 | border-radius: 5px; 239 | border: 0 solid #000; 240 | color: #FC0; 241 | padding: 5px; 242 | overflow: scroll; 243 | margin: 0; 244 | height: 35vh 245 | } 246 | pre.console a { 247 | color: #6f6 248 | } 249 | .console-in { 250 | background-color: #fff; 251 | border-radius: 5px; 252 | border: 1px solid #d3d2d2; 253 | color: #000; 254 | padding: 5px; 255 | width: 100%; 256 | height: 100%; 257 | box-sizing: border-box 258 | } 259 | .console-in option:checked { 260 | background-image: -webkit-linear-gradient(#e6e7e8, #e6e7e8); 261 | background-image: linear-gradient(#e6e7e8, #e6e7e8) 262 | } 263 | .flex-hbox, 264 | .flex-vbox { 265 | display: -webkit-box; 266 | display: flex; 267 | display: -ms-flexbox; 268 | display: -webkit-flex 269 | } 270 | .flex-hbox { 271 | -webkit-box-orient: horizontal; 272 | -webkit-box-direction: normal; 273 | -webkit-flex-direction: row; 274 | -ms-flex-direction: row; 275 | flex-direction: row; 276 | ms-flex-direction: row; 277 | webkit-flex-direction: row 278 | } 279 | .flex-vbox { 280 | -webkit-box-orient: vertical; 281 | -webkit-box-direction: normal; 282 | -webkit-flex-direction: column; 283 | -ms-flex-direction: column; 284 | flex-direction: column; 285 | ms-flex-direction: column; 286 | webkit-flex-direction: column 287 | } 288 | .flex-fill { 289 | -webkit-box-flex: 1; 290 | -webkit-flex: 1 1 auto; 291 | -ms-flex: 1 1 auto; 292 | flex: 1 1 auto; 293 | ms-flex: 1 1 auto; 294 | webkit-flex: 1 1 auto 295 | } 296 | .height100 { 297 | height: 100% 298 | } 299 | .dbg-btn, 300 | #refresh-button { 301 | vertical-align: baseline; 302 | width: 48%; 303 | float: left 304 | } 305 | .dbg-btn, 306 | #reset-button { 307 | vertical-align: baseline; 308 | width: 48%; 309 | float: left 310 | } 311 | .dbg-btn, 312 | #log-reset-button { 313 | vertical-align: baseline; 314 | width: 48%; 315 | float: left 316 | } 317 | #reset-button { 318 | text-transform: none !important 319 | } 320 | .lock-icon { 321 | background-image: url("img/icons.png"); 322 | background-color: transparent; 323 | width: 32px; 324 | height: 32px; 325 | display: inline-block 326 | } 327 | #menu { 328 | margin-left: -150px; 329 | width: 20vw; 330 | position: fixed; 331 | top: 0; 332 | left: 0; 333 | bottom: 0; 334 | z-index: 1000; 335 | background: #007481; 336 | overflow: auto; 337 | -webkit-overflow-scrolling: touch 338 | } 339 | #menu a { 340 | color: #fff; 341 | border: 0; 342 | padding: .6em 0 .7em .6em 343 | } 344 | #menu .pure-menu, 345 | #menu .pure-menu ul { 346 | border: 0; 347 | background: transparent 348 | } 349 | #menu .pure-menu-selected a { 350 | color: #fff 351 | } 352 | #menu .pure-menu-heading { 353 | font-size: 110%; 354 | color: #fff; 355 | margin: 0; 356 | text-transform: none 357 | } 358 | #menu .pure-menu-heading img { 359 | vertical-align: middle; 360 | top: -1px; 361 | position: relative; 362 | margin: 15% 25% 363 | } 364 | #menu .pure-menu-item, 365 | .sub-menu-header { 366 | line-height: 4vh; 367 | padding-left: 2.5vh; 368 | text-transform: uppercase; 369 | border-bottom: 1px solid #00979c 370 | } 371 | .menu-link { 372 | position: fixed; 373 | display: block; 374 | top: 0; 375 | left: 0; 376 | background: #000; 377 | background: rgba(0, 0, 0, 0.7); 378 | font-size: 10px; 379 | z-index: 10; 380 | width: 2em; 381 | height: auto; 382 | padding: 2.1em 1.6em 383 | } 384 | .menu-link span { 385 | position: relative; 386 | display: block 387 | } 388 | .menu-link span, 389 | .menu-link span:before, 390 | .menu-link span:after { 391 | background-color: #fff; 392 | width: 100%; 393 | height: .2em 394 | } 395 | .menu-link span:before, 396 | .menu-link span:after { 397 | position: absolute; 398 | margin-top: -0.6em; 399 | content: " " 400 | } 401 | .menu-link span:after { 402 | margin-top: .6em 403 | } 404 | @media(min-width:56em) { 405 | .header, .content { 406 | padding-left: 2em; 407 | padding-right: 2em 408 | } 409 | #layout { 410 | padding-left: 16%; 411 | left: 0 412 | } 413 | #menu { 414 | left: 150px 415 | } 416 | .menu-link { 417 | position: fixed; 418 | left: 150px; 419 | display: none 420 | } 421 | #layout.active .menu-link { 422 | left: 150px 423 | } 424 | } 425 | @media(max-width:56em) { 426 | #layout.active { 427 | position: relative; 428 | left: 150px 429 | } 430 | } 431 | @media (max-height: 600px){ 432 | #version { 433 | visibility : hidden; 434 | } 435 | } 436 | #messages { 437 | position: absolute; 438 | left: 25%; 439 | width: 50%; 440 | top: 10; 441 | z-index: 200; 442 | font-size: 110%; 443 | text-align: center 444 | } 445 | #warning { 446 | background-color: #933; 447 | color: #fcc; 448 | padding: .1em .4em; 449 | position: fixed; 450 | margin-left: 25% 451 | } 452 | #notification { 453 | background-color: #693; 454 | color: #cfc; 455 | padding: .1em .4em; 456 | position: fixed; 457 | margin-left: 25% 458 | } 459 | #spinner { 460 | position: absolute; 461 | right: 10%; 462 | top: 20; 463 | z-index: 1000 464 | } 465 | .spinner { 466 | height: 50px; 467 | width: 50px; 468 | -webkit-animation: rotation 1s infinite linear; 469 | animation: rotation 1s infinite linear; 470 | border-left: 10px solid rgba(204, 51, 0, 0.15); 471 | border-right: 10px solid rgba(204, 51, 0, 0.15); 472 | border-bottom: 10px solid rgba(204, 51, 0, 0.15); 473 | border-top: 10px solid rgba(204, 51, 0, 0.8); 474 | border-radius: 100% 475 | } 476 | .spinner-small { 477 | display: inline-block; 478 | height: 1em; 479 | width: 1em; 480 | border-width: 4px 481 | } 482 | @-webkit-keyframes rotation { 483 | from { 484 | -webkit-transform: rotate(0) 485 | } 486 | to { 487 | -webkit-transform: rotate(359deg) 488 | } 489 | } 490 | @keyframes rotation { 491 | from { 492 | -webkit-transform: rotate(0); 493 | transform: rotate(0) 494 | } 495 | to { 496 | -webkit-transform: rotate(359deg); 497 | transform: rotate(359deg) 498 | } 499 | } 500 | #version { 501 | font-size: 10pt; 502 | bottom: 0; 503 | color: #00979c; 504 | font-weight: normal !important; 505 | text-align: center; 506 | padding: 0 7%; 507 | /*font-family: TyponineSansMonoText;*/ 508 | position: fixed; 509 | margin-bottom: 2%; 510 | margin-top: 80% 511 | } 512 | hr { 513 | border-color: #00979c 514 | } 515 | p.sub-menu-header { 516 | color: #00979c !important; 517 | /*font-family: TyponineSansMonoText;*/ 518 | font-size: 9pt; 519 | padding-left: 4%; 520 | padding-top: 6%; 521 | text-transform: uppercase; 522 | height: 30px; 523 | margin-bottom: 0 524 | } 525 | .pure-menu-link:hover, 526 | li.pure-menu-item.pure-menu-selected, 527 | li.pure-menu-item:hover { 528 | background-color: #00979c !important 529 | } 530 | td.td_key { 531 | color: #797979 !important 532 | } 533 | .pure-table { 534 | width: 100%; 535 | background-color: #f5f6f6 536 | } 537 | .header p { 538 | padding: 0 14.5% 0 11%; 539 | text-align: left 540 | } 541 | #change-hostname-button, 542 | #change-network-button { 543 | width: 100% !important; 544 | float: left 545 | } 546 | #main { 547 | width: 80vw; 548 | right: 0; 549 | position: absolute 550 | } 551 | .system-name { 552 | float: left; 553 | width: 100%; 554 | text-align: center 555 | } 556 | .td_header { 557 | background-color: #e6e7e8 !important 558 | } 559 | span.jl { 560 | padding: 0 10%; 561 | float: left; 562 | width: 100% 563 | } 564 | legend { 565 | color: #797979 566 | } 567 | .card h1 { 568 | text-transform: uppercase; 569 | font-size: 1.5em !important 570 | } 571 | #slip-enable-checkbox { 572 | float: left 573 | } 574 | #slip-enable-label { 575 | text-transform: uppercase 576 | } 577 | #menu-list { 578 | padding-top: 10% 579 | } 580 | .console-options { 581 | float: right; 582 | text-align: -webkit-right; 583 | margin: 8px 0 584 | } 585 | button.pure-button.button-primary { 586 | width: 100%; 587 | text-transform: uppercase 588 | } 589 | #change-hostname-input { 590 | width: 99%; 591 | margin-top: 16px; 592 | border-radius: 4px; 593 | border: 1px solid #ccc; 594 | height: 30px 595 | } 596 | #hostname-card td { 597 | border: 0 598 | } 599 | a.pure-button.button-primary { 600 | text-transform: uppercase; 601 | width: 100% 602 | } 603 | #wifiform, 604 | #specform, 605 | #sliptext, 606 | #slip-enable-div { 607 | padding: 0 18px 608 | } 609 | #enable-status-report, 610 | form#specform { 611 | padding-top: 3% !important 612 | } 613 | .content .pure-g { 614 | width: 80%; 615 | margin: 0 10% 616 | } 617 | td.td_header { 618 | padding-left: 0 619 | } 620 | .td_header h1 { 621 | color: #007481; 622 | /*font-family: TyponineSansMonoMedium !important;*/ 623 | font-size: 13pt !important; 624 | padding-left: .7em 625 | } 626 | .pure-button { 627 | /*font-family: TyponineSansMonoMedium !important;*/ 628 | font-size: .8rem !important 629 | } 630 | .pure-g.serial-monitor-input { 631 | margin: 0 !important; 632 | margin-right: 10.5%; 633 | width: 100% 634 | } 635 | .pure-g.debug { 636 | margin: 0; 637 | width: 100% 638 | } 639 | a.pure-menu-link { 640 | /*font-family: TyponineSansMonoMedium;*/ 641 | font-size: 13pt 642 | } 643 | #send-history, 644 | #input-text { 645 | width: 100% 646 | } 647 | .console-box { 648 | margin-left: 10.2% !important; 649 | margin-right: 10.2% !important 650 | } 651 | .button-larger-margin { 652 | margin-bottom: 10px; 653 | margin-top: 10px 654 | } 655 | #slip-enable-div { 656 | padding-bottom: 10px 657 | } 658 | .abutton { 659 | padding-bottom: 15% !important; 660 | padding-top: 5% !important 661 | } 662 | #clear-console-button { 663 | vertical-align: baseline; 664 | width: 48%; 665 | float: right 666 | } 667 | .dbg-log { 668 | width: 30% !important 669 | } 670 | .dbg-log#clear-console-button, 671 | .dbg-log#log-reset-button { 672 | margin-left: 5% !important 673 | } 674 | #alertWiFiMode { 675 | display: none; 676 | position: absolute; 677 | background-color: #F00; 678 | color: #FFF; 679 | border-radius: 5px; 680 | max-width: 30%; 681 | padding: .1em .4em 682 | } 683 | 684 | .menu-disabled { 685 | pointer-events: none; 686 | cursor: default; 687 | } 688 | 689 | /*@font-face {*/ 690 | /*font-family: TyponineSansMonoMedium;*/ 691 | /*src: url("/fonts/TypSansMono-Medium.otf")*/ 692 | /*}*/ 693 | /*@font-face {*/ 694 | /*font-family: TyponineSansMonoText;*/ 695 | /*src: url("/fonts/TypSansMono-Text.otf")*/ 696 | /*}*/ -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/ui.js: -------------------------------------------------------------------------------- 1 | var bnd = function (c, b, a) { 2 | c.addEventListener(b, a, false) 3 | }; 4 | var ubnd = function (c, b, a) { 5 | c.removeEventListener(b, a, false) 6 | }; 7 | var m = function (f, d, g) { 8 | d = document; 9 | g = d.createElement("p"); 10 | g.innerHTML = f; 11 | f = d.createDocumentFragment(); 12 | while (d = g.firstChild) { 13 | f.appendChild(d) 14 | } 15 | return f 16 | }; 17 | var $ = function (d, c) { 18 | d = d.match(/^(\W)?(.*)/); 19 | return (c || document)["getElement" + (d[1] ? d[1] == "#" ? "ById" : "sByClassName" : "sByTagName")](d[2]) 20 | }; 21 | var j = function (b) { 22 | for (b = 0; b < 4; b++) { 23 | try { 24 | return b ? new ActiveXObject([, "Msxml2", "Msxml3", "Microsoft"][b] + ".XMLHTTP") : new XMLHttpRequest 25 | } 26 | catch (c) {} 27 | } 28 | }; 29 | 30 | function domForEach(b, a) { 31 | return Array.prototype.forEach.call(b, a) 32 | } 33 | e = function (b) { 34 | return document.createElement(b) 35 | }; 36 | 37 | function onLoad(b) { 38 | var a = window.onload; 39 | if (typeof a != "function") { 40 | window.onload = b 41 | } 42 | else { 43 | window.onload = function () { 44 | a(); 45 | b() 46 | } 47 | } 48 | } 49 | 50 | function addClass(b, a) { 51 | b.className += " " + a 52 | } 53 | 54 | function removeClass(f, c) { 55 | var b = f.className.split(/\s+/) 56 | , a = b.length; 57 | for (var d = 0; d < a; d++) { 58 | if (b[d] === c) { 59 | b.splice(d, 1) 60 | } 61 | } 62 | f.className = b.join(" "); 63 | return b.length != a 64 | } 65 | 66 | function toggleClass(b, a) { 67 | if (!removeClass(b, a)) { 68 | addClass(b, a) 69 | } 70 | } 71 | 72 | function ajaxReq(h, a, g, c) { 73 | var f = j(); 74 | f.open(h, a, true); 75 | var d = setTimeout(function () { 76 | f.abort(); 77 | console.log("XHR abort:", h, a); 78 | f.status = 599; 79 | f.responseText = "request time-out" 80 | }, 9000); 81 | f.onreadystatechange = function () { 82 | if (f.readyState != 4) { 83 | return 84 | } 85 | clearTimeout(d); 86 | if (f.status >= 200 && f.status < 300) { 87 | g(f.responseText) 88 | } 89 | else { 90 | console.log("XHR ERR :", h, a, "->", f.status, f.responseText, f); 91 | c(f.status, f.responseText) 92 | } 93 | }; 94 | try { 95 | f.send() 96 | } 97 | catch (b) { 98 | console.log("XHR EXC :", h, a, "->", b); 99 | c(599, b) 100 | } 101 | } 102 | 103 | function dispatchJson(f, d, c) { 104 | var a; 105 | try { 106 | a = JSON.parse(f) 107 | } 108 | catch (b) { 109 | console.log("JSON parse error: " + b + ". In: " + f); 110 | c(500, "JSON parse error: " + b); 111 | return 112 | } 113 | d(a) 114 | } 115 | 116 | function ajaxJson(d, a, c, b) { 117 | ajaxReq(d, a, function (f) { 118 | dispatchJson(f, c, b) 119 | }, b) 120 | } 121 | 122 | function ajaxSpin(d, a, c, b) { 123 | $("#spinner").removeAttribute("hidden"); 124 | ajaxReq(d, a, function (f) { 125 | $("#spinner").setAttribute("hidden", ""); 126 | c(f) 127 | }, function (f, g) { 128 | $("#spinner").setAttribute("hidden", ""); 129 | b(f, g) 130 | }) 131 | } 132 | 133 | function ajaxJsonSpin(d, a, c, b) { 134 | ajaxSpin(d, a, function (f) { 135 | dispatchJson(f, c, b) 136 | }, b) 137 | } 138 | 139 | function hidePopup(a) { 140 | addClass(a, "popup-hidden"); 141 | addClass(a.parentNode, "popup-target") 142 | } 143 | onLoad(function () { 144 | var b = $("#layout"); 145 | var f = b.childNodes[0]; 146 | b.insertBefore(m(''), f); 147 | b.insertBefore(m('
'), f); 148 | b.insertBefore(m(''), f); 149 | var d = m(' '); 150 | b.insertBefore(d, f); 151 | var g = $("#menuLink") 152 | , d = $("#menu"); 153 | var selmen = document.getElementsByClassName("pure-menu-item"); 154 | if (location.pathname == "/wifi.html") { 155 | toggleClass(selmen[1], "pure-menu-selected") 156 | removeClass(selmen[0], "pure-menu-selected") 157 | } 158 | else { 159 | toggleClass(selmen[0], "pure-menu-selected") 160 | removeClass(selmen[1], "pure-menu-selected") 161 | } 162 | bnd(g, "click", function (i) { 163 | var h = "active"; 164 | i.preventDefault(); 165 | toggleClass(b, h); 166 | toggleClass(d, h); 167 | toggleClass(g, h); 168 | setTimeout(a, 10000) 169 | }); 170 | domForEach($(".popup"), function (h) { 171 | hidePopup(h) 172 | }); 173 | bnd($("#main"), "click", function () { 174 | a() 175 | }); 176 | var a = function () { 177 | removeClass(b, "active"); 178 | removeClass(d, "active"); 179 | removeClass(g, "active") 180 | } 181 | }); 182 | 183 | function showWifiInfo(f) { 184 | Object.keys(f).forEach(function (h) { 185 | el = $("#wifi-" + h); 186 | if (el != null) { 187 | if (el.nodeName === "INPUT") { 188 | el.value = f[h] 189 | } 190 | else { 191 | el.innerHTML = f[h] 192 | } 193 | } 194 | }); 195 | var d = $("#dhcp-r" + f.dhcp); 196 | if (d) { 197 | d.click() 198 | } 199 | var b = $("#wifi-spinner"); 200 | if (b != null) { 201 | b.setAttribute("hidden", "") 202 | } 203 | $("#wifi-table").removeAttribute("hidden"); 204 | var c = $("#change-hostname-input"); 205 | if (c != null) { 206 | c.value = f.hostname 207 | } 208 | currAp = f.ssid; 209 | var g = $("#wifi-warn"); 210 | if (g != null) { 211 | var a = g.children[0]; 212 | if (currAp == "" || currAp == null) { 213 | a.onclick = ""; 214 | bnd(a, "click", function (h) { 215 | showConfigWiFiMessage() 216 | }) 217 | } 218 | } 219 | document.title = f.hostname 220 | } 221 | 222 | function getWifiInfo() { 223 | ajaxJson("GET", "/wifi/info", showWifiInfo, function (b, a) { 224 | window.setTimeout(getWifiInfo, 1000) 225 | }) 226 | } 227 | 228 | function setEditToClick(a, b) { 229 | domForEach($("." + a), function (c) { 230 | if (c.children.length > 0) { 231 | domForEach(c.children, function (d) { 232 | if (d.nodeName === "INPUT") { 233 | d.value = b 234 | } 235 | else { 236 | if (d.nodeName !== "DIV") { 237 | d.innerHTML = b 238 | } 239 | } 240 | }) 241 | } 242 | else { 243 | c.innerHTML = b 244 | } 245 | }) 246 | } 247 | 248 | function showSystemInfo(a) { 249 | Object.keys(a).forEach(function (b) { 250 | setEditToClick("system-" + b, a[b]) 251 | }); 252 | currAp = a.ssid 253 | } 254 | 255 | function getSystemInfo() { 256 | ajaxJson("GET", "/system/info", showSystemInfo, function (b, a) { 257 | window.setTimeout(getSystemInfo, 1000) 258 | }) 259 | } 260 | 261 | function makeAjaxInput(a, b) { 262 | domForEach($("." + a + "-" + b), function (i) { 263 | var f = $(".edit-on", i); 264 | var d = $(".edit-off", i)[0]; 265 | var c = "/" + a + "/update?" + b; 266 | if (d === undefined || f == undefined) { 267 | return 268 | } 269 | var h = function () { 270 | d.setAttribute("hidden", ""); 271 | domForEach(f, function (k) { 272 | k.removeAttribute("hidden") 273 | }); 274 | f[0].select(); 275 | return false 276 | }; 277 | var g = function (k) { 278 | ajaxSpin("GET", c + "=" + k, function () { 279 | domForEach(f, function (l) { 280 | l.setAttribute("hidden", "") 281 | }); 282 | d.removeAttribute("hidden"); 283 | setEditToClick(a + "-" + b, k); 284 | showNotification(b + " changed to " + k) 285 | }, function () { 286 | showWarning(b + " change failed") 287 | }); 288 | return false 289 | }; 290 | bnd(d, "click", function () { 291 | return h() 292 | }); 293 | bnd(f[0], "blur", function () { 294 | return g(f[0].value) 295 | }); 296 | bnd(f[0], "keyup", function (k) { 297 | if ((k || window.event).keyCode == 13) { 298 | return g(f[0].value) 299 | } 300 | }) 301 | }) 302 | } 303 | 304 | function showWarning(b) { 305 | var a = $("#warning"); 306 | a.innerHTML = b; 307 | a.removeAttribute("hidden"); 308 | window.scrollTo(0, 0) 309 | } 310 | 311 | function hideWarning() { 312 | el = $("#warning").setAttribute("hidden", "") 313 | } 314 | var notifTimeout = null; 315 | 316 | function showNotification(b) { 317 | var a = $("#notification"); 318 | a.innerHTML = b; 319 | a.removeAttribute("hidden"); 320 | if (notifTimeout != null) { 321 | clearTimeout(notifTimeout) 322 | } 323 | notifTimout = setTimeout(function () { 324 | a.setAttribute("hidden", ""); 325 | notifTimout = null 326 | }, 4000) 327 | } 328 | 329 | function changeToWifiPage(a) { 330 | window.location.href = "wifi.html" 331 | } 332 | 333 | function showConfigWiFiMessage() { 334 | alert("Please connect to an existing wifi to enable this function.") 335 | } 336 | 337 | function getBoardInfo() { 338 | ajaxReq("GET", "boardInfo", function (board) { 339 | var b = JSON.parse(board); 340 | $("#logo").src = b.logo; 341 | change_favicon(b.icon); 342 | if ($("#guide") != null) $("#guide").href = b.link; 343 | $("#version").innerHTML = b.fw_name + " - " + b.fw_version + "
" + b.build_date; 344 | }, function () { 345 | console.log("Error during scan") 346 | }); 347 | } 348 | 349 | function change_favicon(img) { 350 | var favicon = document.querySelector('link[rel="shortcut icon"]'); 351 | if (!favicon) { 352 | favicon = document.createElement('link'); 353 | favicon.setAttribute('rel', 'shortcut icon'); 354 | var head = document.querySelector('head'); 355 | head.appendChild(favicon); 356 | } 357 | favicon.setAttribute('type', 'image/png'); 358 | favicon.setAttribute('href', img); 359 | } -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/wifi.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

WiFi Configuration

18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | 27 | 28 | 29 | 31 | 32 | 33 | 36 | 37 | 38 |
26 |

Hostname

30 |
34 | 35 |
39 |
40 |
41 |
42 |
Switch to AP+SPA mode to enable network selection.
43 | 44 | 45 | 46 | 48 | 49 | 50 |
47 |

Wifi Connection

51 | 52 |
53 | To connect to a WiFi network, please select one of the detected networks, enter the password, and hit the connect button... 54 | 55 |
56 | 59 | 60 | 61 | 62 | 63 |
64 |
65 |
66 |
67 |
68 | 69 | 70 | 71 | 72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 111 | 112 | 113 |
73 |

WiFi Status

WiFi channel
Configured network
WiFi status
WiFi address
WiFi rssi (dB)
WiFi phy
WiFi MAC
WiFi mode
109 | 110 |
114 |
115 |
116 |
117 | 118 | 119 | 120 | 122 | 123 | 124 |
121 |

Advanced

125 |
126 |
127 | 129 |
130 | 132 |
133 |
134 | 141 | 142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | 150 | 151 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/data/wifi.js: -------------------------------------------------------------------------------- 1 | var currAp = ""; 2 | var blockScan = 0; 3 | var attempt = 0; 4 | var networkAlert = "This action could need a reboot if you run a sketch that uses network connection. Do you want to proceed?"; 5 | 6 | function createInputForAp(b) { 7 | if (b.essid == "" && b.rssi == 0) { 8 | return 9 | } 10 | var g = e("input"); 11 | g.type = "radio"; 12 | g.name = "essid"; 13 | g.value = b.essid; 14 | g.id = "opt-" + b.essid; 15 | if (currAp == b.essid) { 16 | g.checked = "1" 17 | } 18 | var i = e("div"); 19 | var j = -Math.floor(b.rssi / 51) * 32; 20 | i.className = "lock-icon"; 21 | i.style.backgroundPosition = "0px " + j + "px"; 22 | var d = e("div"); 23 | d.innerHTML = "" + b.rssi + "dB"; 24 | var c = e("div"); 25 | var h = "-64"; 26 | if (b.enc == "0") { 27 | h = "0" 28 | } 29 | if (b.enc == "1") { 30 | h = "-32" 31 | } 32 | c.className = "lock-icon"; 33 | c.style.backgroundPosition = "-32px " + h + "px"; 34 | var f = e("div"); 35 | f.innerHTML = b.essid; 36 | var a = m('').childNodes[0]; 37 | a.appendChild(g); 38 | a.appendChild(c); 39 | a.appendChild(i); 40 | a.appendChild(d); 41 | a.appendChild(f); 42 | return a 43 | } 44 | 45 | function getSelectedEssid() { 46 | var c = document.forms.wifiform.elements; 47 | for (var b = 0; b < c.length; b++) { 48 | if (c[b].type == "radio" && c[b].checked) { 49 | var a = c[b].value; 50 | if (a == "_hidden_ssid_") { 51 | a = $("#hidden-ssid").value 52 | } 53 | return a 54 | } 55 | } 56 | return currAp 57 | } 58 | var scanTimeout = null; 59 | var scanReqCnt = 0; 60 | 61 | function scanResult() { 62 | if (scanReqCnt > 60) { 63 | return scanAPs() 64 | } 65 | scanReqCnt += 1; 66 | ajaxJson("GET", "wifi/scan", function (c) { 67 | currAp = getSelectedEssid(); 68 | if (c.result.APs.length > 0) { 69 | $("#aps").innerHTML = ""; 70 | var d = 0; 71 | for (var b = 0; b < c.result.APs.length; b++) { 72 | if (c.result.APs[b].essid == "" && c.result.APs[b].rssi == 0) { 73 | continue 74 | } 75 | $("#aps").appendChild(createInputForAp(c.result.APs[b])); 76 | d = d + 1 77 | } 78 | enableNetworkSelection(); 79 | $("#spinner").setAttribute("hidden", ""); 80 | showNotification("Scan found " + d + " networks"); 81 | var a = $("#connect-button"); 82 | a.className = a.className.replace(" pure-button-disabled", ""); 83 | if (scanTimeout != null) { 84 | clearTimeout(scanTimeout) 85 | } 86 | } 87 | else { 88 | showWarning("AP's not found") 89 | } 90 | }, function (b, a) { 91 | $("#spinner").setAttribute("hidden", ""); 92 | showWarning("Please rescan") 93 | }) 94 | } 95 | 96 | function scanAPs() { 97 | $("#spinner").removeAttribute("hidden"); 98 | window.scrollTo(0, 0); 99 | if (blockScan) { 100 | scanTimeout = window.setTimeout(scanAPs, 1500); 101 | return 102 | } 103 | ajaxReq("GET", "wifi/netNumber", function (a){ 104 | scanTimeout = null; 105 | scanReqCnt = 0; 106 | scanResult(); 107 | }, function(b,a){ 108 | $("#spinner").setAttribute("hidden", ""); 109 | showWarning("Error during scanning, please retry.") 110 | }) 111 | 112 | } 113 | 114 | function getStatus() { 115 | ajaxJsonSpin("GET", "connstatus", function (c) { 116 | if (c.status == "idle" || c.status == "connecting") { 117 | $("#aps").innerHTML = "Connecting..."; 118 | showNotification("Connecting..."); 119 | window.setTimeout(getStatus, 1000) 120 | } 121 | else { 122 | if (c.status == "got IP address") { 123 | var a = "Connected! Got IP " + c.ip; 124 | showNotification(a); 125 | showWifiInfo(c); 126 | blockScan = 0; 127 | if (c.modechange == "yes") { 128 | var b = "esp will switch to STA-only mode in a few seconds"; 129 | window.setTimeout(function () { 130 | showNotification(b) 131 | }, 4000) 132 | } 133 | $("#reconnect").removeAttribute("hidden"); 134 | $("#reconnect").innerHTML = 'If you are in the same network, go to ' + c.ip + ", else connect to network " + c.ssid + " first." 135 | } 136 | else { 137 | blockScan = 0; 138 | showWarning("Connection failed"); 139 | $("#aps").innerHTML = 'Check password and selected AP. Go Back' 140 | } 141 | } 142 | enableNetworkSelection() 143 | }, function (b, a) { 144 | if (attempt < 3) { 145 | showWarning("Problems in connection...I'm trying again"); 146 | window.setTimeout(hideWarning, 3000); 147 | window.setTimeout(getStatus, 2000); 148 | attempt++; 149 | } 150 | else { 151 | showWarning("Connection failed.\nConnect to AP"); 152 | attempt = 0; 153 | blockScan = 0; 154 | } 155 | }) 156 | } 157 | 158 | function changeWifiMode(a) { 159 | if (confirm(networkAlert)) { 160 | blockScan = 1; 161 | hideWarning(); 162 | ajaxSpin("GET", "setmode?mode=" + a, function (b) { 163 | showNotification("Mode changed"); 164 | window.setTimeout(getWifiInfo, 100); 165 | blockScan = 0; 166 | window.setTimeout(enableNetworkSelection, 500) 167 | }, function (c, b) { //b is the error message, sometimes is empty 168 | showWarning("Error changing mode "); 169 | window.setTimeout(getWifiInfo, 100); 170 | blockScan = 0; 171 | window.setTimeout(enableNetworkSelection, 500) 172 | }) 173 | } 174 | } 175 | 176 | function changeWifiAp(d) { 177 | if (confirm(networkAlert)) { 178 | d.preventDefault(); 179 | var b = $("#wifi-passwd").value; 180 | var f = getSelectedEssid(); 181 | showNotification("Connecting to " + f); 182 | var c = "connect?essid=" + encodeURIComponent(f) + "&passwd=" + encodeURIComponent(b); 183 | hideWarning(); 184 | $("#reconnect").setAttribute("hidden", ""); 185 | $("#wifi-passwd").value = ""; 186 | var a = $("#connect-button"); 187 | var g = a.className; 188 | a.className += " pure-button-disabled"; 189 | blockScan = 1; 190 | ajaxSpin("GET", c, function (h) { 191 | if(h==1) 192 | window.setTimeout(function(){ 193 | $("#spinner").removeAttribute("hidden"); 194 | showNotification("Waiting for network change..."); 195 | window.scrollTo(0, 0); 196 | window.setTimeout(getStatus, 2000) 197 | },10000); 198 | }, function (i, h) { 199 | showWarning("Error switching network: " + h); 200 | a.className = g; 201 | blockScan = 0; 202 | }) 203 | } 204 | } 205 | 206 | function changeSpecial(c) { 207 | if (confirm(networkAlert)) { 208 | c.preventDefault(); 209 | var b = "special"; 210 | b += "?dhcp=" + document.querySelector('input[name="dhcp"]:checked').value; 211 | b += "&staticip=" + encodeURIComponent($("#wifi-staticip").value); 212 | b += "&netmask=" + encodeURIComponent($("#wifi-netmask").value); 213 | b += "&gateway=" + encodeURIComponent($("#wifi-gateway").value); 214 | hideWarning(); 215 | var a = $("#special-button"); 216 | addClass(a, "pure-button-disabled"); 217 | ajaxSpin("GET", b, function (d) { 218 | removeClass(a, "pure-button-disabled") 219 | if (d != 1) { 220 | alert("New IP set, you will be redirect to: " + JSON.parse(d).url); 221 | setTimeout(document.location.href = "http://" + JSON.parse(d).url + "/wifi.html", 1000); 222 | } 223 | else showNotification("DHCP set"); 224 | }, function (f, d) { 225 | showWarning("Error: " + d); 226 | removeClass(a, "pure-button-disabled"); 227 | getWifiInfo(); 228 | }) 229 | } 230 | } 231 | 232 | function changeHostname() { 233 | if (confirm(networkAlert)) { 234 | var a = $("#change-hostname-input").value; 235 | if (a == "") { 236 | alert("Insert hostname!") 237 | } 238 | else { 239 | ajaxSpin("GET", "/system/update?name=" + a, function () { 240 | showHostnameModal(a) 241 | }) 242 | } 243 | } 244 | } 245 | 246 | function showHostnameModal(b) { 247 | var a = "Hostname changed in : " + b; //+ "\nYour board will be reboot to apply change"; 248 | var c = confirm(a); 249 | if (c == false) alert("Error in hostname change"); 250 | } 251 | 252 | function hostnameLimitations(c) { 253 | var b = new RegExp("^[a-zA-Z0-9\-_\b]+$"); 254 | var a = String.fromCharCode(!c.charCode ? c.which : c.charCode); 255 | if (!b.test(a)) { 256 | c.preventDefault(); 257 | return false 258 | } 259 | } 260 | 261 | function enableNetworkSelection() { 262 | ajaxJson("GET", "/wifi/info", function (j) { 263 | var a = (j.mode == "STA"); 264 | var h = document.getElementById("wifiform") 265 | , c = h.getElementsByTagName("input") 266 | , f = $("#connect-button") 267 | , s = $("#scan-button"); 268 | var g, d = 0; 269 | while (g = c[d++]) { 270 | g.disabled = a 271 | } 272 | f.disabled = a; 273 | s.disabled = a; 274 | if (a) { 275 | bnd(h, "mouseover", displayWiFiModeAlert); 276 | bnd(h, "mouseout", hideWiFiModeAlert) 277 | toggleClass(f, "pure-button-disabled") 278 | } 279 | else { 280 | ubnd(h, "mouseover", displayWiFiModeAlert); 281 | ubnd(h, "mouseout", hideWiFiModeAlert) 282 | removeClass(f, "pure-button-disabled") 283 | } 284 | }) 285 | } 286 | 287 | function displayWiFiModeAlert() { 288 | $("#alertWiFiMode").style.display = "inherit" 289 | } 290 | 291 | function hideWiFiModeAlert() { 292 | $("#alertWiFiMode").style.display = "none" 293 | } 294 | 295 | function doDhcp() { 296 | $("#dhcp-on").removeAttribute("hidden"); 297 | $("#dhcp-off").setAttribute("hidden", "") 298 | } 299 | 300 | function doStatic() { 301 | $("#dhcp-off").removeAttribute("hidden"); 302 | $("#dhcp-on").setAttribute("hidden", "") 303 | }; -------------------------------------------------------------------------------- /ArduinoFirmwareEsp/utility/wifi_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | wifi_utils.h - Library for Arduino WiFi ESP8266 Firmware 3 | Copyright (c) 2016 Arduino Srl. All right reserved. 4 | 5 | based on: 6 | 7 | wifi_spi.h - Library for Arduino Wifi shield. 8 | Copyright (c) 2011-2014 Arduino. All right reserved. 9 | 10 | This library is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU Lesser General Public 12 | License as published by the Free Software Foundation; either 13 | version 2.1 of the License, or (at your option) any later version. 14 | 15 | This library is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | Lesser General Public License for more details. 19 | 20 | You should have received a copy of the GNU Lesser General Public 21 | License along with this library; if not, write to the Free Software 22 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 | */ 24 | 25 | #ifndef H_WIFI_UTILS_H 26 | #define H_WIFI_UTILS_H 27 | 28 | #include 29 | 30 | #define CMD_FLAG 0 31 | #define REPLY_FLAG 1<<7 32 | #define DATA_FLAG 0x40 33 | 34 | #define WIFI_SPI_ACK 1 35 | #define WIFI_SPI_ERR 0xFF 36 | 37 | //#define TIMEOUT_CHAR 1000 38 | 39 | #define MAX_SOCK_NUMBER 4 /**< Maxmium number of socket */ 40 | #define NO_SOCKET_AVAIL 255 41 | 42 | #define START_CMD 0xE0 43 | #define END_CMD 0xEE 44 | #define ERR_CMD 0xEF 45 | 46 | #define RESPONSE_LENGHT 384 //array for response length 47 | 48 | #define SPI_BUFFER_SIZE 128 49 | #define SPI_DATA_READY 1 50 | #define SPI_DATA_RECEIVED 2 51 | #define SLAVE_READY_PIN 5 52 | 53 | enum numParams{ 54 | PARAM_NUMS_0 = 0, 55 | PARAM_NUMS_1, 56 | PARAM_NUMS_2, 57 | PARAM_NUMS_3, 58 | PARAM_NUMS_4, 59 | PARAM_NUMS_5, 60 | MAX_PARAM_NUMS 61 | }; 62 | 63 | enum sizeParams{ 64 | PARAM_SIZE_0 = 0, 65 | PARAM_SIZE_1, 66 | PARAM_SIZE_2, 67 | PARAM_SIZE_3, 68 | PARAM_SIZE_4, 69 | PARAM_SIZE_5, 70 | PARAM_SIZE_6 71 | }; 72 | 73 | 74 | #define MAX_PARAMS MAX_PARAM_NUMS-1 75 | //#define PARAM_LEN_SIZE 1 76 | 77 | typedef struct __attribute__((__packed__)) 78 | { 79 | uint8_t paramLen; 80 | uint8_t param[128]; 81 | //String param; 82 | //char* param; 83 | } tParam; 84 | 85 | typedef struct __attribute__((__packed__)) 86 | { 87 | uint16_t dataLen; 88 | uint8_t data[128]; 89 | //char* data; 90 | } tDataParam; 91 | 92 | typedef struct __attribute__((__packed__)) 93 | { 94 | unsigned char cmd; 95 | unsigned char tcmd; 96 | unsigned char nParam; 97 | tParam params[MAX_PARAMS]; 98 | tDataParam paramsData[MAX_PARAMS]; 99 | } tMsgPacket;//tSpiMsg; 100 | 101 | typedef struct __attribute__((__packed__)) 102 | { 103 | unsigned char cmd; 104 | unsigned char tcmd; 105 | unsigned char nParam; 106 | tDataParam params[MAX_PARAMS]; 107 | } tMsgPacketData;//tSpiMsgData; 108 | 109 | // typedef struct __attribute__((__packed__)) 110 | // { 111 | // unsigned char cmd; 112 | // unsigned char tcmd; 113 | // //unsigned char totLen; 114 | // unsigned char nParam; 115 | // } tSpiHdr; 116 | // 117 | // typedef struct __attribute__((__packed__)) 118 | // { 119 | // uint8_t paramLen; 120 | // uint32_t param; 121 | // } tLongParam; 122 | // 123 | // typedef struct __attribute__((__packed__)) 124 | // { 125 | // uint8_t paramLen; 126 | // uint16_t param; 127 | // } tIntParam; 128 | // 129 | // typedef struct __attribute__((__packed__)) 130 | // { 131 | // uint8_t paramLen; 132 | // uint8_t param; 133 | // } tByteParam; 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2016-2017 Arduino Srl http://www.arduino.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WiFi Link - ESP8266 firmware for Arduino 2 | 3 | This is a fork of Arduino.org WiFi Link firmware. The goal of this fork is make improvements and provide documentation. 4 | 5 | The WiFi Link firmware is an ESP8266 arduino sketch developed by Arduino.org in Arduino IDE using Arduino esp8266 core. It was developed for the Arduino Star Otto, Arduino Primo and [Uno WiFi Developer Edition](https://github.com/jandrassy/UnoWiFiDevEdSerial1). 6 | 7 | With changes in this fork, it can be used with any esp8266 in combination with an Arduino MCU. 8 | 9 | The corresponding Arduino library is [WiFi Link library](https://github.com/jandrassy/arduino-library-wifilink). 10 | 11 | ## Building from source code 12 | 13 | The WiFi Link firmware is an Arduino sketch so you can build it in Arduino IDE and upload it to ESP from Arduino IDE. 14 | 15 | Building WiFi Link firmware from source files gives you possibility to build the newest version, build a branch version, build some fork version or change something in source code you need. 16 | 17 | You need the esp8266 core for Arduino IDE. To install it using boards manager, follow the [instructions](https://github.com/esp8266/Arduino#installing-with-boards-manager). 18 | 19 | Additionally, install the [Arduino ESP8266 filesystem uploader IDE plugin](https://github.com/esp8266/arduino-esp8266fs-plugin#arduino-esp8266-filesystem-uploader-) 20 | 21 | ### Download the source code 22 | 23 | Use the source code from this fork. Every GitHub repository has a green "Clone or download" button which opens a small menu. Choose "Download ZIP". 24 | 25 | Open or extract the downloaded zip and copy the folder ArduinoFirmwareEsp from zip to your sketches folder. 26 | 27 | Start Arduino IDE and open the ArduinoFirmwareEsp.ino sketch. It opens additional files as tabs in IDE. 28 | 29 | ![WiFiLink network port](https://github.com/jandrassy/arduino-firmware-wifilink/wiki/images/IDE-WiFiLink-fw.png) 30 | 31 | ### Board selection and Verify 32 | 33 | In Tools menu select board options and choose your board/shield/module or Generic ESP8266 from the ESP8266 section of the Boards menu. Set hardware depended options like crystal frequency, flash frequency, flash mode, flash size, led, reset. 34 | 35 | Then choose esp8266 usage options: 36 | - CPU frequency option - recommended 80 MHz; 160 MHz produces more heat and has a higher power consumption 37 | - LwIP version - v2 Higher Bandwidth 38 | - Debug options: Disable and None is the basic setting 39 | - Optimal flash size selection for WiFi Link is "4M (1M SPIFFS)". at least 256 kB is needed for SPIFFS 40 | - "Erase flash" option is new in esp8266 Arduino package 2.4.1. To preserve SPIFFS and WiFi credentials use option "Sketch only". If changing from prebuild firmware, changing LwIP option or after a update to new version of esp8266 Arduino core package use "All flash content" to erase all parameters set be Espressif SDK. Erasing all SDK parameters can help if you experience WiFi connection stability issues. 41 | 42 | Now verify the sketch with the Verify button. The first compilation after changing the board will take time. 43 | 44 | ### Upload 45 | 46 | [Setup](https://github.com/jandrassy/arduino-firmware-wifilink/wiki/Test-Setup) your esp board/shield/module for flashing. Connect it to USB of the computer and if it doesn't support DTR, put it into programming mode with dedicated button. 47 | 48 | Use the Upload button in IDE to upload the WiFi Link firmware. 49 | 50 | ### SPIFFS 51 | 52 | SPIFFS is the file system for ESP8266. 53 | 54 | In subfolder `data` of the source codes of the WiFi Link firmware are the static web files (html, css, js) for the Web Panel. You can add your own files and they will be accessible on expected url. 55 | 56 | We installed a plugin tool in chapter "Install esp8266 packages". This plugin creates a "ESP8266 Sketch Data upload" command in Tools menu. 57 | 58 | The tool builds the SPIFFS binary and uploads it to selected port. With serial port you must put your board/shield/module into flashing mode in it doesn't support reset on DTR. Then choose "ESP8266 Sketch Data upload" from Tools menu. 59 | 60 | ### Web Panel 61 | 62 | Before connecting to WebPanel after serial flashing of firmware, power cycle the module/board/shield. 63 | 64 | Web Panel network configuration uses the configuration Access Point (AP). User connects to the WiFi network created by a device, goes to fixed IP address URL and configures the network access in a Web Panel. The device connects in STA mode to the selected WiFi network and is accessible at IP address assigned by a DHCP or a static address set in Web Panel. 65 | 66 | The AP network name is set as SSIDNAME in config.h. The WiFi Link fixed address in AP mode is http://192.168.240.1/. 67 | 68 | Warning: If you connect from sketch with WiFi.begin(ssid, pass), it changes the settings for the STA mode of the esp8266 for the Web Panel too. If you connect from sketch to unaccesible network or with wrong password, the Web Panel will not be accessible in STA mode. 69 | 70 | ### Firmware OTA upload 71 | 72 | The WiFi Link firmware supports OTA upload of new version of the firmware binary. OTA upload will only work if some version of WiFi Link is working in the ESP8266 and is configured to STA or STA+AP mode. 73 | 74 | After configuring WiFi Link to WiFi STA or STA+AP mode, the IDE will detect it on network using mdns. The network 'port' will be accessible in Port submenu of Tools menu. Choose the network port for the OTA upload and use the Upload button in IDE. 75 | 76 | *Notes: Do not put the board in bootloader mode. Do not search for some special programmer in Tools menu.* 77 | 78 | The upload of the ArduinoFirmwareEsp.ino will overwrite the firmware binary and leave the SPIFFS part of the flash unchanged. 79 | 80 | OTA upload works with SPIFS too and is fast. 81 | 82 | ### config.json 83 | 84 | WiFi Link firmware writes hostname and static IP settings into SPIFFS file config.json. SPIFFS upload overrides the SPIFFS content and the setting are lost. 85 | 86 | If you have not default hostname or a static IP configured and you often upload the SPIFFS, download `http:///config.json` and add it to `data` subfolder of the WiFi Link firmware source codes. 87 | 88 | ### Atmega328p sketch OTA upload support 89 | 90 | *For ATmega boards with at least 64 kB flash and for some ARM boards (SAMD, nRF52), you can use [ArduinoOTA library](https://github.com/jandrassy/ArduinoOTA) for upload over network with WiFiLink installed.* 91 | 92 | The WiFi Link firmware build with `#define MCU_OTA` (config.h) supports ATmega328p sketch OTA upload. To build from the source codes with `MCU_OTA` you need a library called dfu. Download it [here](https://github.com/jandrassy/arduino-firmware-wifilink/wiki/lib/dfu.zip) and Install it to your libraries folder. 93 | 94 | For OTA with esp8266 module/board/shield, the reset pin of the ATmega must be [connected](https://github.com/jandrassy/arduino-firmware-wifilink/wiki/Test-Setup) to an ESP GPIO pin. Default in dfu library in esp8266-serial-arduinouno-hacked.cpp is GPIO5. You can change it to GPIO0, as it is on pinout header on most ESP modules. Star Otto and Uno WiFi have special setting hardcoded. 95 | 96 | Only settings for the ATmega328p with Uno bootloader (Optiboot) exist in the dfu library. 97 | 98 | Select "Arduino Uno WiFi" as board for network upload to Uno/Nano/Mini and other ATmega328p boards with Optiboot. 99 | 100 | ![WiFiLink network port](https://github.com/jandrassy/arduino-firmware-wifilink/wiki/images/UnoWiFi-netport.png) 101 | 102 | ## SPI connection 103 | 104 | WiFi Link can be used for [MCU connected to esp8266 over SPI](https://github.com/jandrassy/arduino-firmware-wifilink/wiki/images/uno-wemos-spi_bb.png). To use it for any esp8266 module/shield/board, change the ESP_CH_UART to ESP_CH_SPI in the GENERIC_ESP8266 section and for the library add `uno.build.extra_flags=-DESP_CH_SPI' to avr boards.txt. 105 | 106 | SPI pins are on the ICSP header of UNO/Mega/Nano (digital pins 11, 12, 13). Pin 10 is needed as SPI SLAVESELECT and pin 7 is used by WiFi Link as SLAVEREADY signal from the esp8266 GPIO pin 5 (D1). 107 | 108 | SPI connection is used for AVR ISP (In System Programming). WiFi Link with MCU_OTA option and dfu library can write a sketch uploaded OTA to Atmega with SPI. But it is complicated. Write an issue on WiFi Link firmware, if you need it working. 109 | 110 | --------------------------------------------------------------------------------