├── ESP8266_Weather_Display.ino ├── ESP8266_Weather_Feather_Huzzah.ino ├── README.md └── SoftwareSerial.cpp /ESP8266_Weather_Display.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define SSID "" // insert your SSID 5 | #define PASS "" // location id 7 | #define DST_IP "23.222.152.140" //api.wunderground.com 8 | 9 | #define ESP_RST 4 10 | 11 | 12 | SoftwareSerial lcdSerial(2, 3); // RX, TX for debugging 13 | 14 | const int buffer=300; 15 | 16 | int passNum = 1; 17 | 18 | // Array of desired weather conditions 19 | // These must be in the order received from wunderground! 20 | // 21 | // Also, watch out for repeating field names in returned json structures, 22 | // and fields with embedded commas (used as delimiters) 23 | 24 | 25 | char* conds[]={"\"city\":","\"weather\":","\"temp_f\":","\"relative_humidity\":","\"wind_dir\":","\"wind_mph\":","\"pressure_in\":"}; 26 | int num_elements = 7; // number of conditions you are retrieving, count of elements in conds 27 | 28 | char close_brace ='}'; 29 | char comma = ','; 30 | 31 | void setup() 32 | { pinMode(ESP_RST,OUTPUT); 33 | reset(); 34 | 35 | Serial.begin(9600); 36 | Serial.setTimeout(5000); 37 | lcdSerial.begin(9600); // Print to LCD (or similar display) 38 | 39 | } 40 | void loop() 41 | { 42 | reset(); 43 | delay(5000); //ESP8266 takes a while to restart 44 | 45 | // try to connect to wifi 46 | boolean connected=false; 47 | for(int i=0;i<5;i++){ 48 | if(connectWiFi()){ 49 | connected = true; 50 | break; 51 | } 52 | } 53 | 54 | Serial.println("AT+CIPMUX=0"); // set to single connection mode 55 | String cmd = "AT+CIPSTART=\"TCP\",\""; 56 | cmd += DST_IP; 57 | cmd += "\",80"; 58 | Serial.println(cmd); 59 | //lcdSerial.println(cmd); 60 | if(Serial.find("Error")) return; 61 | 62 | cmd = "GET /api//conditions/q/"; 63 | cmd += LOCATIONID; 64 | cmd +=".json"; 65 | cmd += " HTTP/1.1\r\nHost: api.wunderground.com\r\n\r\n"; 66 | Serial.print(F("AT+CIPSEND=")); 67 | Serial.println(cmd.length()); 68 | if(Serial.find(">")){ 69 | //lcdSerial.print(">"); 70 | }else{ 71 | Serial.println(F("AT+CIPCLOSE")); 72 | lcdSerial.println(F("?fConnection timeout")); 73 | delay(1000); 74 | return; 75 | } 76 | Serial.print(cmd); 77 | 78 | unsigned int i = 0; //timeout counter 79 | char json[buffer]="{"; // array for Json parsing 80 | int n = 1; // character counter for json 81 | 82 | for (int j=0;j jsonBuffer; 124 | JsonObject& root = jsonBuffer.parseObject(json); 125 | 126 | if (!root.success()) 127 | { 128 | lcdSerial.print("?fparseObject() failed"); 129 | //return; 130 | } 131 | 132 | 133 | const char* city = root["city"]; 134 | const char* weather = root["weather"]; 135 | double temp_f = root["temp_f"]; 136 | const char* humidity = root["relative_humidity"]; 137 | const char* wind_dir = root["wind_dir"]; 138 | double wind_mph = root["wind_mph"]; 139 | const char* pressure_in = root["pressure_in"]; 140 | 141 | 142 | // location of conditions 143 | lcdSerial.print("?f "); 144 | lcdSerial.print(city); 145 | 146 | // Conditions: Sunny, Cloudy, Fog, Rain, etc. 147 | lcdSerial.print(F("?n")); 148 | lcdSerial.print(weather); 149 | 150 | // Temp 151 | lcdSerial.print(F("?n")); 152 | lcdSerial.print((int)temp_f); 153 | lcdSerial.print(F("F|")); 154 | 155 | // Humidity 156 | lcdSerial.print(humidity); 157 | lcdSerial.print(F("|")); 158 | 159 | // Wind Direction 160 | lcdSerial.print(wind_dir); 161 | lcdSerial.print(F(" ")); 162 | 163 | // Wind Speed 164 | lcdSerial.print((int)wind_mph); 165 | lcdSerial.print(F(" ")); 166 | lcdSerial.print(F("MPH")); 167 | 168 | 169 | // Barometric Pressure 170 | lcdSerial.print(F("?nBarometer: ")); 171 | lcdSerial.print(pressure_in); 172 | 173 | } 174 | 175 | 176 | void reset() 177 | { 178 | digitalWrite(ESP_RST,LOW); 179 | delay(100); 180 | digitalWrite(ESP_RST,HIGH); 181 | 182 | } 183 | 184 | 185 | boolean connectWiFi() 186 | { 187 | Serial.println(F("AT+CWMODE=1")); 188 | String cmd="AT+CWJAP=\""; 189 | cmd+=SSID; 190 | cmd+="\",\""; 191 | cmd+=PASS; 192 | cmd+="\""; 193 | Serial.println(cmd); 194 | delay(2000); 195 | if(Serial.find("OK")){ 196 | lcdSerial.println(F("?fConnected to WiFi...")); 197 | delay(500); // Allow display to print 198 | return true; 199 | }else{ 200 | lcdSerial.println(F("?fCan't Connect...")); 201 | delay(500); 202 | return false; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /ESP8266_Weather_Feather_Huzzah.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Changes Based on: 3 | * https://github.com/mike-rankin/ESP8266_OLED_Display 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #define SSID "Your SSID" // insert your SSID 10 | #define PASS "Your Password" // insert your password 11 | #define LOCATIONID "94523" // location id 12 | //#define HOST "23.222.152.140" //api.wunderground.com 13 | #define HOST "api.wunderground.com" 14 | #define YOUR_KEY "INSERT your wunderground api key HERE" // your (free) license_key 15 | 16 | const int buffer_size = 300; // length of json buffer 17 | 18 | SoftwareSerial lcdSerial(4, 5); // RX, TX for debugging 19 | 20 | const int buffer=300; 21 | 22 | int passNum = 1; 23 | 24 | // Array of desired weather conditions 25 | // These must be in the order received from wunderground! 26 | // 27 | // Also, watch out for repeating field names in returned json structures, 28 | // and fields with embedded commas (used as delimiters) 29 | 30 | 31 | char* conds[]={ 32 | "\"city\":", 33 | "\"weather\":", 34 | "\"temp_f\":", 35 | "\"relative_humidity\":", 36 | "\"wind_dir\":", 37 | "\"wind_mph\":", 38 | "\"pressure_in\":" 39 | }; 40 | 41 | int num_elements = 7; // number of conditions you are retrieving, count of elements in conds 42 | 43 | unsigned long WMillis = 0; // temporary millis() register 44 | 45 | void setup() 46 | { Serial.begin(115200); // baudrate of monitor 47 | WiFi.begin(SSID,PASS); // your WiFi Network's SSID & Password 48 | while (WiFi.status() != WL_CONNECTED) { // DO until connected 49 | delay(500); // 50 | Serial.print("."); // print a few dots 51 | } 52 | Serial.println(""); 53 | Serial.println("WiFi connected"); 54 | Serial.println("IP address: "); 55 | Serial.println(WiFi.localIP()); 56 | 57 | 58 | lcdSerial.begin(9600); // Print to LCD (or similar display) 59 | wunderground(); 60 | } 61 | 62 | 63 | void loop() 64 | { 65 | // Only check weather every 5-15 minutes, so you don't go over quota on wunderground (for free api license) 66 | if (millis()-WMillis >= 600000) { // 300 seconds interval 67 | wunderground(); // get new data 68 | WMillis=millis(); // 69 | } 70 | // enough time here to do other stuff, like a bar that is showing how long it takes to update. 71 | } 72 | 73 | void wunderground() 74 | { 75 | Serial.print("connecting to "); 76 | Serial.println(HOST); 77 | 78 | // Use WiFiClient class to create TCP connections 79 | WiFiClient client; 80 | 81 | const int httpPort = 80; 82 | 83 | if (!client.connect(HOST, httpPort)) { 84 | Serial.println("connection failed"); 85 | return; 86 | } 87 | 88 | String cmd = "GET /api/"; cmd += YOUR_KEY; // build request_string cmd 89 | cmd += "/conditions/q/"; cmd += LOCATIONID; cmd +=".json"; // 90 | cmd += " HTTP/1.1\r\nHost: api.wunderground.com\r\n\r\n"; // 91 | delay(500); 92 | client.print(cmd); // connect to api.wunderground.com with request_string 93 | delay(500); 94 | unsigned int i = 0; // timeout counter 95 | char json[buffer_size]="{"; // first character for json-string is begin-bracket 96 | int n = 1; // character counter for json 97 | 98 | for (int j=0;j jsonBuffer; 140 | JsonObject& root = jsonBuffer.parseObject(json); 141 | 142 | if (!root.success()) 143 | { 144 | lcdSerial.print("?fparseObject() failed"); 145 | //return; 146 | } 147 | 148 | 149 | const char* city = root["city"]; 150 | const char* weather = root["weather"]; 151 | double temp_f = root["temp_f"]; 152 | const char* humidity = root["relative_humidity"]; 153 | const char* wind_dir = root["wind_dir"]; 154 | double wind_mph = root["wind_mph"]; 155 | const char* pressure_in = root["pressure_in"]; 156 | 157 | 158 | // location of conditions 159 | lcdSerial.print("?f "); 160 | lcdSerial.print(city); 161 | Serial.println(city); 162 | // Conditions: Sunny, Cloudy, Fog, Rain, etc. 163 | lcdSerial.print(F("?n")); 164 | lcdSerial.print(weather); 165 | Serial.println(weather); 166 | // Temp 167 | lcdSerial.print(F("?n")); 168 | lcdSerial.print((int)temp_f); 169 | lcdSerial.print(F("F|")); 170 | Serial.println((int)temp_f); 171 | // Humidity 172 | lcdSerial.print(humidity); 173 | lcdSerial.print(F("|")); 174 | 175 | // Wind Direction 176 | lcdSerial.print(wind_dir); 177 | lcdSerial.print(F(" ")); 178 | 179 | // Wind Speed 180 | lcdSerial.print((int)wind_mph); 181 | lcdSerial.print(F(" ")); 182 | lcdSerial.print(F("MPH")); 183 | 184 | 185 | // Barometric Pressure 186 | lcdSerial.print(F("?nBarometer: ")); 187 | lcdSerial.print(pressure_in); 188 | 189 | } 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **ESP8266 Weather Display** 2 | ## *Description:* 3 | This is an Arduino project that uses the cheap ESP8266 WiFi module. 4 | For more details see the blog posting: http://thisoldgeek.blogspot.com/2015/01/esp8266-weather-display.html 5 | 6 | 7 | The sketch connects to wunderground.com and returns current weather conditions 8 | for your location every 15 minutes. The returned data is formatted as JSON. The 9 | JSON is parsed for display on a serial LCD. 10 | 11 | ## *Configuration:* 12 | * You will need an API key (free) from http://www.wunderground.com/weather/api/d/login.html 13 | * You must install ArduinoJson in your sketchbook/libraries folder (https://github.com/bblanchon/ArduinoJson) 14 | * You must be using Arduino 1.0.6 or later for ArduinoJson library to install properly - it uses nested include statements 15 | * Change the following: 16 | * SSID - name of your network 17 | * PASS - password for your network 18 | * LOCATIONID - desired geographic location for weather conditions (eg., US ZipCode, State/City, Int'l) 19 | * YOURKEY - your API key for wunderground 20 | 21 | **LOCATIONID** can be of the following forms: 22 | * #define LOCATIONID "Germany/Munich" (International) 23 | * #define LOCATIONID "94523" (US ZipCode) 24 | * #define LOCATIONID "CA/Pleasant_Hill" (US City) 25 | 26 | ## *Process:* 27 | * Hard Reset 28 | * Connect to WiFi 29 | * Set to single connection 30 | * Set up TCP connection 31 | * Send data (GET) to wunderground 32 | * Use Serial to read the returned json (JavaScript Object Notation) pairs 33 | * Parse the key-value pairs 34 | * Display 35 | * Close the connection 36 | * Wait 15 minutes (to keep within limits of free API license) 37 | 38 | ## *Pro Trinket Notes Updated 01-07-2015* 39 | The Pro Trinket 3V runs at 12Mhz. SoftwareSerial (required for this project) through Arduino 1.0.6 40 | only has a version of SoftwareSerial that supports 8, 16 and 20MHz. You will need to update the 41 | SoftwareSerial.cpp file, available here. 42 | 43 | Instructions for updating SoftwareSerial.cpp on a Mac: 44 | * Close Arduino if running 45 | * Open a New Finder Window 46 | * Open Applications, highlight Arduino (or whatever you name your Arduino app) 47 | * Show Package Contents 48 | * Open Resources\Java\libraries\SoftwareSerial 49 | * Copy SoftwareSerial.cpp (downloaded from github) to this folder, replacing the older version of SoftwareSerial.cpp 50 | * Restart Arduino 51 | 52 | ## *Update April 29, 2016 for Adafruit Feather Huzzah:* 53 | This code uses the ESP8266WiFi library to do Web client calls via the Feather Huzzah. The older version in this git 54 | does straight-up serial AT Command Set calls to the ESP8266 module. 55 | 56 | The code update here is based on Mike Rankin's ESP8266 OLED Weather Display: 57 | https://github.com/mike-rankin/ESP8266_OLED_Display 58 | 59 | My version still prints to a 4x20 LCD display. You will still need to use an LM317 voltage regulator as described in 60 | the blog post at http://thisoldgeek.blogspot.com/2015/01/esp8266-weather-display.html. 61 | This is because the 4x20 LCD requires 5v at a hight current than the Feather Huzzah can supply. 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /SoftwareSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | SoftwareSerial.cpp (formerly NewSoftSerial.cpp) - 3 | Multi-instance software serial library for Arduino/Wiring 4 | -- Interrupt-driven receive and other improvements by ladyada 5 | (http://ladyada.net) 6 | -- Tuning, circular buffer, derivation from class Print/Stream, 7 | multi-instance support, porting to 8MHz processors, 8 | various optimizations, PROGMEM delay tables, inverse logic and 9 | direct port writing by Mikal Hart (http://www.arduiniana.org) 10 | -- Pin change interrupt macros by Paul Stoffregen (http://www.pjrc.com) 11 | -- 20MHz processor support by Garrett Mace (http://www.macetech.com) 12 | -- ATmega1280/2560 support by Brett Hagman (http://www.roguerobotics.com/) 13 | 14 | This library is free software; you can redistribute it and/or 15 | modify it under the terms of the GNU Lesser General Public 16 | License as published by the Free Software Foundation; either 17 | version 2.1 of the License, or (at your option) any later version. 18 | 19 | This library is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 22 | Lesser General Public License for more details. 23 | 24 | You should have received a copy of the GNU Lesser General Public 25 | License along with this library; if not, write to the Free Software 26 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 27 | 28 | The latest version of this library can always be found at 29 | http://arduiniana.org. 30 | */ 31 | 32 | // When set, _DEBUG co-opts pins 11 and 13 for debugging with an 33 | // oscilloscope or logic analyzer. Beware: it also slightly modifies 34 | // the bit times, so don't rely on it too much at high baud rates 35 | #define _DEBUG 0 36 | #define _DEBUG_PIN1 11 37 | #define _DEBUG_PIN2 13 38 | // 39 | // Includes 40 | // 41 | #include 42 | #include 43 | #include 44 | #include 45 | // 46 | // Lookup table 47 | // 48 | typedef struct _DELAY_TABLE 49 | { 50 | long baud; 51 | unsigned short rx_delay_centering; 52 | unsigned short rx_delay_intrabit; 53 | unsigned short rx_delay_stopbit; 54 | unsigned short tx_delay; 55 | } DELAY_TABLE; 56 | 57 | #if F_CPU == 16000000 58 | 59 | static const DELAY_TABLE PROGMEM table[] = 60 | { 61 | // baud rxcenter rxintra rxstop tx 62 | { 115200, 1, 17, 17, 12, }, 63 | { 57600, 10, 37, 37, 33, }, 64 | { 38400, 25, 57, 57, 54, }, 65 | { 31250, 31, 70, 70, 68, }, 66 | { 28800, 34, 77, 77, 74, }, 67 | { 19200, 54, 117, 117, 114, }, 68 | { 14400, 74, 156, 156, 153, }, 69 | { 9600, 114, 236, 236, 233, }, 70 | { 4800, 233, 474, 474, 471, }, 71 | { 2400, 471, 950, 950, 947, }, 72 | { 1200, 947, 1902, 1902, 1899, }, 73 | { 600, 1902, 3804, 3804, 3800, }, 74 | { 300, 3804, 7617, 7617, 7614, }, 75 | }; 76 | 77 | const int XMIT_START_ADJUSTMENT = 5; 78 | 79 | #elif F_CPU == 12000000 80 | 81 | static const DELAY_TABLE PROGMEM table[] = 82 | { 83 | // baud rxcenter rxintra rxstop tx 84 | { 115200, 1, 13, 13, 9, }, 85 | { 57600, 9, 28, 28, 24, }, 86 | { 38400, 16, 43, 43, 39, }, 87 | { 31250, 21, 53, 53, 49, }, 88 | { 28800, 24, 58, 58, 54, }, 89 | { 19200, 39, 87, 87, 83, }, 90 | { 14400, 54, 117, 117, 113, }, 91 | { 9600, 83, 177, 177, 173, }, 92 | { 4800, 173, 355, 355, 351, }, 93 | { 2400, 351, 712, 712, 708, }, 94 | { 1200, 708, 1427, 1427, 1423, }, 95 | { 300, 2851, 5712, 5712, 5708, }, 96 | }; 97 | 98 | const int XMIT_START_ADJUSTMENT = 5; 99 | 100 | #elif F_CPU == 8000000 101 | 102 | static const DELAY_TABLE table[] PROGMEM = 103 | { 104 | // baud rxcenter rxintra rxstop tx 105 | { 115200, 1, 5, 5, 3, }, 106 | { 57600, 1, 15, 15, 13, }, 107 | { 38400, 2, 25, 26, 23, }, 108 | { 31250, 7, 32, 33, 29, }, 109 | { 28800, 11, 35, 35, 32, }, 110 | { 19200, 20, 55, 55, 52, }, 111 | { 14400, 30, 75, 75, 72, }, 112 | { 9600, 50, 114, 114, 112, }, 113 | { 4800, 110, 233, 233, 230, }, 114 | { 2400, 229, 472, 472, 469, }, 115 | { 1200, 467, 948, 948, 945, }, 116 | { 600, 948, 1895, 1895, 1890, }, 117 | { 300, 1895, 3805, 3805, 3802, }, 118 | }; 119 | 120 | const int XMIT_START_ADJUSTMENT = 4; 121 | 122 | #elif F_CPU == 20000000 123 | 124 | // 20MHz support courtesy of the good people at macegr.com. 125 | // Thanks, Garrett! 126 | 127 | static const DELAY_TABLE PROGMEM table[] = 128 | { 129 | // baud rxcenter rxintra rxstop tx 130 | { 115200, 3, 21, 21, 18, }, 131 | { 57600, 20, 43, 43, 41, }, 132 | { 38400, 37, 73, 73, 70, }, 133 | { 31250, 45, 89, 89, 88, }, 134 | { 28800, 46, 98, 98, 95, }, 135 | { 19200, 71, 148, 148, 145, }, 136 | { 14400, 96, 197, 197, 194, }, 137 | { 9600, 146, 297, 297, 294, }, 138 | { 4800, 296, 595, 595, 592, }, 139 | { 2400, 592, 1189, 1189, 1186, }, 140 | { 1200, 1187, 2379, 2379, 2376, }, 141 | { 600, 2379, 4759, 4759, 4755, }, 142 | { 300, 4759, 9523, 9523, 9520, }, 143 | }; 144 | 145 | const int XMIT_START_ADJUSTMENT = 6; 146 | 147 | #else 148 | 149 | #error This version of SoftwareSerial supports only 20, 16 and 8MHz processors 150 | 151 | #endif 152 | 153 | // 154 | // Statics 155 | // 156 | SoftwareSerial *SoftwareSerial::active_object = 0; 157 | char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; 158 | volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0; 159 | volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; 160 | 161 | // 162 | // Debugging 163 | // 164 | // This function generates a brief pulse 165 | // for debugging or measuring on an oscilloscope. 166 | inline void DebugPulse(uint8_t pin, uint8_t count) 167 | { 168 | #if _DEBUG 169 | volatile uint8_t *pport = portOutputRegister(digitalPinToPort(pin)); 170 | 171 | uint8_t val = *pport; 172 | while (count--) 173 | { 174 | *pport = val | digitalPinToBitMask(pin); 175 | *pport = val; 176 | } 177 | #endif 178 | } 179 | 180 | // 181 | // Private methods 182 | // 183 | 184 | /* static */ 185 | inline void SoftwareSerial::tunedDelay(uint16_t delay) { 186 | uint8_t tmp=0; 187 | 188 | asm volatile("sbiw %0, 0x01 \n\t" 189 | "ldi %1, 0xFF \n\t" 190 | "cpi %A0, 0xFF \n\t" 191 | "cpc %B0, %1 \n\t" 192 | "brne .-10 \n\t" 193 | : "+r" (delay), "+a" (tmp) 194 | : "0" (delay) 195 | ); 196 | } 197 | 198 | // This function sets the current object as the "listening" 199 | // one and returns true if it replaces another 200 | bool SoftwareSerial::listen() 201 | { 202 | if (active_object != this) 203 | { 204 | _buffer_overflow = false; 205 | uint8_t oldSREG = SREG; 206 | cli(); 207 | _receive_buffer_head = _receive_buffer_tail = 0; 208 | active_object = this; 209 | SREG = oldSREG; 210 | return true; 211 | } 212 | 213 | return false; 214 | } 215 | 216 | // 217 | // The receive routine called by the interrupt handler 218 | // 219 | void SoftwareSerial::recv() 220 | { 221 | 222 | #if GCC_VERSION < 40302 223 | // Work-around for avr-gcc 4.3.0 OSX version bug 224 | // Preserve the registers that the compiler misses 225 | // (courtesy of Arduino forum user *etracer*) 226 | asm volatile( 227 | "push r18 \n\t" 228 | "push r19 \n\t" 229 | "push r20 \n\t" 230 | "push r21 \n\t" 231 | "push r22 \n\t" 232 | "push r23 \n\t" 233 | "push r26 \n\t" 234 | "push r27 \n\t" 235 | ::); 236 | #endif 237 | 238 | uint8_t d = 0; 239 | 240 | // If RX line is high, then we don't see any start bit 241 | // so interrupt is probably not for us 242 | if (_inverse_logic ? rx_pin_read() : !rx_pin_read()) 243 | { 244 | // Wait approximately 1/2 of a bit width to "center" the sample 245 | tunedDelay(_rx_delay_centering); 246 | DebugPulse(_DEBUG_PIN2, 1); 247 | 248 | // Read each of the 8 bits 249 | for (uint8_t i=0x1; i; i <<= 1) 250 | { 251 | tunedDelay(_rx_delay_intrabit); 252 | DebugPulse(_DEBUG_PIN2, 1); 253 | uint8_t noti = ~i; 254 | if (rx_pin_read()) 255 | d |= i; 256 | else // else clause added to ensure function timing is ~balanced 257 | d &= noti; 258 | } 259 | 260 | // skip the stop bit 261 | tunedDelay(_rx_delay_stopbit); 262 | DebugPulse(_DEBUG_PIN2, 1); 263 | 264 | if (_inverse_logic) 265 | d = ~d; 266 | 267 | // if buffer full, set the overflow flag and return 268 | if ((_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF != _receive_buffer_head) 269 | { 270 | // save new data in buffer: tail points to where byte goes 271 | _receive_buffer[_receive_buffer_tail] = d; // save new byte 272 | _receive_buffer_tail = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF; 273 | } 274 | else 275 | { 276 | #if _DEBUG // for scope: pulse pin as overflow indictator 277 | DebugPulse(_DEBUG_PIN1, 1); 278 | #endif 279 | _buffer_overflow = true; 280 | } 281 | } 282 | 283 | #if GCC_VERSION < 40302 284 | // Work-around for avr-gcc 4.3.0 OSX version bug 285 | // Restore the registers that the compiler misses 286 | asm volatile( 287 | "pop r27 \n\t" 288 | "pop r26 \n\t" 289 | "pop r23 \n\t" 290 | "pop r22 \n\t" 291 | "pop r21 \n\t" 292 | "pop r20 \n\t" 293 | "pop r19 \n\t" 294 | "pop r18 \n\t" 295 | ::); 296 | #endif 297 | } 298 | 299 | void SoftwareSerial::tx_pin_write(uint8_t pin_state) 300 | { 301 | if (pin_state == LOW) 302 | *_transmitPortRegister &= ~_transmitBitMask; 303 | else 304 | *_transmitPortRegister |= _transmitBitMask; 305 | } 306 | 307 | uint8_t SoftwareSerial::rx_pin_read() 308 | { 309 | return *_receivePortRegister & _receiveBitMask; 310 | } 311 | 312 | // 313 | // Interrupt handling 314 | // 315 | 316 | /* static */ 317 | inline void SoftwareSerial::handle_interrupt() 318 | { 319 | if (active_object) 320 | { 321 | active_object->recv(); 322 | } 323 | } 324 | 325 | #if defined(PCINT0_vect) 326 | ISR(PCINT0_vect) 327 | { 328 | SoftwareSerial::handle_interrupt(); 329 | } 330 | #endif 331 | 332 | #if defined(PCINT1_vect) 333 | ISR(PCINT1_vect) 334 | { 335 | SoftwareSerial::handle_interrupt(); 336 | } 337 | #endif 338 | 339 | #if defined(PCINT2_vect) 340 | ISR(PCINT2_vect) 341 | { 342 | SoftwareSerial::handle_interrupt(); 343 | } 344 | #endif 345 | 346 | #if defined(PCINT3_vect) 347 | ISR(PCINT3_vect) 348 | { 349 | SoftwareSerial::handle_interrupt(); 350 | } 351 | #endif 352 | 353 | // 354 | // Constructor 355 | // 356 | SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) : 357 | _rx_delay_centering(0), 358 | _rx_delay_intrabit(0), 359 | _rx_delay_stopbit(0), 360 | _tx_delay(0), 361 | _buffer_overflow(false), 362 | _inverse_logic(inverse_logic) 363 | { 364 | setTX(transmitPin); 365 | setRX(receivePin); 366 | } 367 | 368 | // 369 | // Destructor 370 | // 371 | SoftwareSerial::~SoftwareSerial() 372 | { 373 | end(); 374 | } 375 | 376 | void SoftwareSerial::setTX(uint8_t tx) 377 | { 378 | pinMode(tx, OUTPUT); 379 | digitalWrite(tx, _inverse_logic ? LOW : HIGH); 380 | _transmitBitMask = digitalPinToBitMask(tx); 381 | uint8_t port = digitalPinToPort(tx); 382 | _transmitPortRegister = portOutputRegister(port); 383 | } 384 | 385 | void SoftwareSerial::setRX(uint8_t rx) 386 | { 387 | pinMode(rx, INPUT); 388 | if (!_inverse_logic) 389 | digitalWrite(rx, HIGH); // pullup for normal logic! 390 | _receivePin = rx; 391 | _receiveBitMask = digitalPinToBitMask(rx); 392 | uint8_t port = digitalPinToPort(rx); 393 | _receivePortRegister = portInputRegister(port); 394 | } 395 | 396 | // 397 | // Public methods 398 | // 399 | 400 | void SoftwareSerial::begin(long speed) 401 | { 402 | _rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0; 403 | 404 | for (unsigned i=0; i