├── IntelHexParse.cpp ├── IntelHexParse.h ├── README.md ├── Stk500.cpp ├── Stk500.h ├── WebServ.cpp ├── WebServ.h ├── data ├── index.htm └── index.htm.gz └── esp_avr_programmer.ino /IntelHexParse.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Arduino.h" 3 | #include "IntelHexParse.h" 4 | 5 | IntelHexParse::IntelHexParse() { 6 | _loadAddress[0] = 0x00; 7 | _loadAddress[1] = 0x00; 8 | } 9 | 10 | void IntelHexParse::ParseLine(byte* hexline) { 11 | _recordType = GetRecordType(hexline); 12 | 13 | if(_recordType == 0) { 14 | _address = GetAddress(hexline); 15 | _length = GetLength(hexline); 16 | 17 | 18 | GetData(hexline, _length); 19 | 20 | if(_memIdx == 128) { 21 | 22 | if(!_firstRun){ 23 | _loadAddress[1] += 0x40; 24 | if(_loadAddress[1] == 0){ 25 | _loadAddress[0] += 0x1; 26 | } 27 | } 28 | _firstRun = false; 29 | _pageReady = true; 30 | _memIdx = 0; 31 | } 32 | 33 | _nextAddress = _address + _length; 34 | 35 | } 36 | 37 | if(_recordType == 1){ 38 | EndOfFile(); 39 | _pageReady=true; 40 | } 41 | 42 | } 43 | 44 | bool IntelHexParse::IsPageReady() { 45 | return _pageReady; 46 | } 47 | 48 | 49 | byte* IntelHexParse::GetMemoryPage() { 50 | _pageReady = false; 51 | return _memoryPage; 52 | } 53 | 54 | byte* IntelHexParse::GetLoadAddress() { 55 | return _loadAddress; 56 | } 57 | 58 | void IntelHexParse::GetLoadAddress(byte* hexline) { 59 | 60 | char buff[3]; 61 | buff[2] = '\0'; 62 | 63 | buff[0] = hexline[3]; 64 | buff[1] = hexline[4]; 65 | _loadAddress[0] = strtol(buff, 0, 16); 66 | buff[0] = hexline[5]; 67 | buff[1] = hexline[6]; 68 | _loadAddress[1] = strtol(buff, 0, 16); 69 | 70 | } 71 | 72 | byte* IntelHexParse::GetData(byte* hexline, int len) { 73 | 74 | int start = 9; 75 | int end = (len * 2) + start; 76 | char buff[3]; 77 | buff[2] = '\0'; 78 | 79 | for(int x = start; x < end; x = x+2) { 80 | buff[0] = hexline[x]; 81 | buff[1] = hexline[x+1]; 82 | _memoryPage[_memIdx] = strtol(buff, 0, 16); 83 | _memIdx++; 84 | } 85 | 86 | } 87 | 88 | void IntelHexParse::EndOfFile() { 89 | 90 | _loadAddress[1] += 0x40; 91 | if(_loadAddress[1] == 0){ 92 | _loadAddress[0] += 0x1; 93 | } 94 | 95 | while(_memIdx < 128) { 96 | _memoryPage[_memIdx] = 0xFF; 97 | _memIdx++; 98 | } 99 | 100 | } 101 | 102 | int IntelHexParse::GetAddress(byte* hexline) { 103 | 104 | char buff[5]; 105 | buff[0] = hexline[3]; 106 | buff[1] = hexline[4]; 107 | buff[2] = hexline[5]; 108 | buff[3] = hexline[6]; 109 | buff[4] = '\0'; 110 | 111 | return strtol(buff, 0, 16); 112 | } 113 | 114 | int IntelHexParse::GetLength(byte* hexline) { 115 | 116 | char buff[3]; 117 | buff[0] = hexline[1]; 118 | buff[1] = hexline[2]; 119 | buff[2] = '\0'; 120 | 121 | return strtol(buff, 0, 16); 122 | } 123 | 124 | int IntelHexParse::GetRecordType(byte* hexline) { 125 | 126 | char buff[3]; 127 | buff[0] = hexline[7]; 128 | buff[1] = hexline[8]; 129 | buff[2] = '\0'; 130 | 131 | return strtol(buff, 0, 16); 132 | } 133 | 134 | 135 | -------------------------------------------------------------------------------- /IntelHexParse.h: -------------------------------------------------------------------------------- 1 | #ifndef IntelHexParse_h 2 | #define IntelHexParse_h 3 | 4 | #include "Arduino.h" 5 | 6 | class IntelHexParse { 7 | 8 | public: 9 | IntelHexParse(); 10 | void ParseLine(byte* data); 11 | byte* GetMemoryPage(); 12 | byte* GetLoadAddress(); 13 | bool IsPageReady(); 14 | 15 | private: 16 | int _address = 0; 17 | int _length = 0; 18 | int _nextAddress = 0; 19 | int _memIdx = 0; 20 | int _recordType = 0; 21 | byte _memoryPage[128]; 22 | byte _loadAddress[2]; 23 | bool _pageReady = false; 24 | bool _firstRun = true; 25 | 26 | int GetAddress(byte* hexline); 27 | int GetLength(byte* hexline); 28 | int GetRecordType(byte* hexline); 29 | byte* GetData(byte* hexline, int len); 30 | void GetLoadAddress(byte* hexline); 31 | void EndOfFile(); 32 | 33 | 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp_avr_programmer 2 | ESP8266 as wireless AVR (arduino) programmer 3 | 4 | The program allows you to upload hex files to the ESP and use to them to flash an arduino, the files are stores on the ESP using the 3MB SPIFFS, the arduino is flashed but connection to the ESP with a browser (only tested with chrome). 5 | 6 | 1. Change the SSID and PASSWORD in code to give the ESP access to the wireless network. 7 | 2. Compile and upload the program using the Arduino IDE. 8 | 3. Install the Arduino IDE SPIFFS tool and use the tools>ESP8266 Data Upload, sends the html files to the ESP. 9 | 4. Connect the ESP TX/RX pins to the RX/TX pins on the arduino, and the ESP pin 4 to the Arduino reset pin. 10 | 5. Use a browser to connect to the ESP and upload a hex files, when uploaded click the flash link to flash the arduino. 11 | -------------------------------------------------------------------------------- /Stk500.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "Stk500.h" 3 | 4 | Stk500::Stk500(int resetPin){ 5 | 6 | Serial.begin(115200); 7 | while (!Serial) { 8 | ; 9 | } 10 | 11 | pinMode(resetPin, OUTPUT); 12 | //pinMode(9, OUTPUT); 13 | //pinMode(0, INPUT); 14 | //pinMode(1, OUTPUT); 15 | _resetPin = resetPin; 16 | 17 | } 18 | 19 | 20 | void Stk500::setupDevice() { 21 | resetMCU(); 22 | getSync(); 23 | setProgParams(); 24 | setExtProgParams(); 25 | enterProgMode(); 26 | 27 | } 28 | 29 | void Stk500::flashPage(byte* address, byte* data) { 30 | 31 | byte header[] = { 0x64, 0x00, 0x80, 0x46 }; 32 | loadAddress(address[1], address[0]); 33 | 34 | Serial.write(header, 4); 35 | for (int i = 0; i < 128; i++) { 36 | Serial.write(data[i]); 37 | } 38 | Serial.write(0x20); 39 | 40 | waitForSerialData(2, 1000); 41 | Serial.read(); 42 | Serial.read(); 43 | } 44 | 45 | void Stk500::resetMCU() { 46 | 47 | digitalWrite(_resetPin, LOW); 48 | delay(1); 49 | digitalWrite(_resetPin, HIGH); 50 | delay(100); 51 | digitalWrite(_resetPin, LOW); 52 | delay(1); 53 | digitalWrite(_resetPin, HIGH); 54 | delay(100); 55 | 56 | } 57 | 58 | int Stk500::getSync() { 59 | 60 | return execCmd(0x30); 61 | 62 | } 63 | 64 | int Stk500::enterProgMode() { 65 | 66 | return execCmd(0x50); 67 | 68 | } 69 | 70 | int Stk500::exitProgMode() { 71 | 72 | return execCmd(0x51); 73 | 74 | } 75 | 76 | int Stk500::setExtProgParams() { 77 | 78 | byte params[] = { 0x05, 0x04, 0xd7, 0xc2, 0x00 }; 79 | return execParam(0x45, params, sizeof(params)); 80 | 81 | } 82 | 83 | int Stk500::setProgParams() { 84 | 85 | byte params[] = { 0x86, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x03, 0xff, 0xff, 0xff, 0xff, 0x00, 0x80, 0x04, 0x00, 0x00, 0x00, 0x80, 0x00 }; 86 | return execParam(0x42, params, sizeof(params)); 87 | 88 | } 89 | 90 | int Stk500::loadAddress(byte adrHi, byte adrLo) { 91 | 92 | byte params[] = { adrHi, adrLo }; 93 | return execParam(0x55, params, sizeof(params)); 94 | 95 | } 96 | 97 | byte Stk500::execCmd(byte cmd) { 98 | 99 | byte bytes[] = { cmd, 0x20 }; 100 | return sendBytes(bytes, 2); 101 | } 102 | 103 | byte Stk500::execParam(byte cmd, byte* params, int count) { 104 | 105 | byte bytes[32]; 106 | bytes[0] = cmd; 107 | 108 | int i = 0; 109 | while (i < count) { 110 | bytes[i + 1] = params[i]; 111 | i++; 112 | } 113 | 114 | bytes[i + 1] = 0x20; 115 | 116 | return sendBytes(bytes, i + 2); 117 | } 118 | 119 | byte Stk500::sendBytes(byte* bytes, int count) { 120 | 121 | Serial.write(bytes, count); 122 | waitForSerialData(2, 1000); 123 | 124 | byte sync = Serial.read(); 125 | byte ok = Serial.read(); 126 | if (sync == 0x14 && ok == 0x10) { 127 | return 1; 128 | } 129 | 130 | return 0; 131 | 132 | } 133 | 134 | int Stk500::waitForSerialData(int dataCount, int timeout) { 135 | 136 | int timer = 0; 137 | 138 | while (timer < timeout) { 139 | if (Serial.available() >= dataCount) { 140 | return 1; 141 | } 142 | delay(1); 143 | timer++; 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | -------------------------------------------------------------------------------- /Stk500.h: -------------------------------------------------------------------------------- 1 | #ifndef Stk500_h 2 | #define Stk500_h 3 | 4 | #include "Arduino.h" 5 | 6 | class Stk500 { 7 | 8 | public: 9 | Stk500(int resPin); 10 | void resetMCU(); 11 | int getSync(); 12 | int enterProgMode(); 13 | int exitProgMode(); 14 | int loadAddress(byte adrHi, byte adrLo); 15 | int setProgParams(); 16 | int setExtProgParams(); 17 | 18 | void setupDevice(); 19 | void flashPage(byte* loadAddress, byte* data); 20 | 21 | private: 22 | byte execCmd(byte cmd); 23 | byte execParam(byte cmd, byte* params, int count); 24 | byte sendBytes(byte* bytes, int count); 25 | int waitForSerialData(int dataCount, int timeout); 26 | int getFlashPageCount(byte flashData[][131]); 27 | 28 | int _resetPin; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /WebServ.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "WebServ.h" 3 | #include 4 | #include "FS.h" 5 | #include "IntelHexParse.h" 6 | #include "Stk500.h" 7 | 8 | 9 | WebServ::WebServ(int resetPin) { 10 | _resetPin = resetPin; 11 | } 12 | 13 | void WebServ::WSCmdIndex(WiFiClient* client) { 14 | 15 | SPIFFS.begin(); 16 | File file = SPIFFS.open("/index.htm.gz", "r"); 17 | 18 | if(file) { 19 | 20 | int fs = file.size(); 21 | byte buff[1024]; 22 | int i = 0; 23 | 24 | client->print(DefaultHeader(true)); 25 | while(fs > 0) { 26 | i = (fs < 1024) ? fs : 1024; 27 | file.read(buff, i); 28 | client->write((const uint8_t*)buff, i); 29 | fs -= 1024; 30 | } 31 | client->print(DefaultFooter()); 32 | 33 | } else { 34 | String html = HttpSimplePage(F("spiffs error: index.htm not found.")); 35 | PrintPage(client, html); 36 | } 37 | 38 | file.close(); 39 | SPIFFS.end(); 40 | } 41 | 42 | void WebServ::WSCmdList(WiFiClient* client) { 43 | 44 | String text = HttpRawText(GetDirList()); 45 | PrintPage(client, text); 46 | } 47 | 48 | void WebServ::WSCmdDelete(WiFiClient* client, String filename) { 49 | 50 | SPIFFS.begin(); 51 | SPIFFS.remove(filename); 52 | SPIFFS.end(); 53 | 54 | String text = HttpRawText(GetDirList()); 55 | PrintPage(client, text); 56 | } 57 | 58 | void WebServ::WSCmdFlash(WiFiClient* client, String filename) { 59 | 60 | Stk500 stk500(_resetPin); 61 | 62 | SPIFFS.begin(); 63 | 64 | File file = SPIFFS.open(filename, "r"); 65 | 66 | if(file) { 67 | stk500.setupDevice(); 68 | IntelHexParse hexParse = IntelHexParse(); 69 | 70 | while(file.available()) { 71 | 72 | byte buff[50]; 73 | String data = file.readStringUntil('\n'); 74 | data.getBytes(buff, data.length()); 75 | hexParse.ParseLine(buff); 76 | 77 | if(hexParse.IsPageReady()){ 78 | byte* page = hexParse.GetMemoryPage(); 79 | byte* address = hexParse.GetLoadAddress(); 80 | stk500.flashPage(address, page); 81 | } 82 | } 83 | } 84 | 85 | stk500.exitProgMode(); 86 | file.close(); 87 | SPIFFS.end(); 88 | } 89 | 90 | 91 | void WebServ::WSCmdUpload(WiFiClient* client, String filename) { 92 | 93 | int contentLen = 0; 94 | 95 | while (client->connected()) { 96 | if (client->available()) { 97 | String line = client->readStringUntil('\n'); 98 | 99 | if(line.startsWith("Content-Length")) { 100 | contentLen = line.substring(16, (line.length()-1)).toInt(); 101 | } 102 | 103 | if(line.length() == 1 && line[0] == '\r') { 104 | 105 | SPIFFS.begin(); 106 | String path = "/hex/" + filename; 107 | File file = SPIFFS.open(path, "w+"); 108 | 109 | if(file) { 110 | int i = 0; 111 | while (i < contentLen) { 112 | file.write(client->read()); 113 | i++; 114 | } 115 | file.close(); 116 | } 117 | SPIFFS.end(); 118 | 119 | delay(10); 120 | String html = HttpSimplePage("DONE"); 121 | client->println(html); 122 | delay(10); 123 | 124 | break; 125 | } 126 | } 127 | } 128 | } 129 | 130 | int WebServ::GetCommand(String s) { 131 | 132 | if(s.startsWith("GET /files")){ 133 | return httpCmdList; 134 | } else if (s.startsWith("GET /delete")) { 135 | return httpCmdDelete; 136 | } else if (s.startsWith("GET /flash")) { 137 | return httpCmdFlash; 138 | } else if (s.startsWith("GET /delete")) { 139 | return httpCmdDelete; 140 | } else if (s.startsWith("POST /upload")) { 141 | return httpCmdUpload; 142 | } else { 143 | return httpCmdIndex; 144 | } 145 | 146 | } 147 | 148 | String WebServ::GetUrlParam(String s) { 149 | 150 | String param = ""; 151 | if(s.indexOf("&") > -1) { 152 | int pStart = s.indexOf("&") +1; 153 | int pEnd = s.indexOf(" ", pStart); 154 | param = s.substring(pStart, pEnd); 155 | } 156 | 157 | return param; 158 | 159 | } 160 | 161 | String WebServ::HttpSimplePage(String text) { 162 | 163 | String html = DefaultHeader(false); 164 | html += "" + text + ""; 165 | html += DefaultFooter(); 166 | 167 | return html; 168 | 169 | } 170 | 171 | String WebServ::HttpRawText(String text) { 172 | 173 | String html = DefaultHeader(false); 174 | html += text; 175 | html += DefaultFooter(); 176 | 177 | return html; 178 | 179 | } 180 | 181 | 182 | String WebServ::DefaultHeader(bool gzip) { 183 | 184 | if(gzip){ 185 | return String(F("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Encoding: gzip\r\nAccess-Control-Allow-Origin: *\r\nConnection: close\r\n\r\n")); 186 | } 187 | return String(F("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nAccess-Control-Allow-Origin: *\r\nConnection: close\r\n\r\n")); 188 | 189 | } 190 | 191 | String WebServ::DefaultFooter() { 192 | 193 | return String(F("\r\n")); 194 | 195 | } 196 | 197 | void WebServ::PrintPage(WiFiClient* client, String page) { 198 | while (client->connected()) { 199 | if (client->available()) { 200 | String line = client->readStringUntil('\r'); 201 | if (line.length() == 1 && line[0] == '\n') { 202 | client->println(page); 203 | break; 204 | } 205 | } 206 | } 207 | } 208 | 209 | String WebServ::GetDirList() { 210 | 211 | String list = ""; 212 | SPIFFS.begin(); 213 | Dir dir = SPIFFS.openDir("/hex"); 214 | while (dir.next()) { 215 | list += dir.fileName() + ";"; 216 | File f = dir.openFile("r"); 217 | list += String(f.size()) + ";\n"; 218 | } 219 | SPIFFS.end(); 220 | return list; 221 | } 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /WebServ.h: -------------------------------------------------------------------------------- 1 | #ifndef WebServ_h 2 | #define WebServ_h 3 | 4 | #include "Arduino.h" 5 | #include "WebServ.h" 6 | #include 7 | #include "FS.h" 8 | #include "IntelHexParse.h" 9 | #include "Stk500.h" 10 | 11 | 12 | class WebServ { 13 | 14 | public: 15 | 16 | const int httpCmdIndex = 0; 17 | const int httpCmdFlash = 1; 18 | const int httpCmdUpload = 2; 19 | const int httpCmdDelete = 3; 20 | const int httpCmdList = 4; 21 | 22 | WebServ(int i); 23 | int GetCommand(String s); 24 | String GetUrlParam(String s); 25 | String HttpSimplePage(String s); 26 | String HttpRawText(String s); 27 | void WSCmdIndex(WiFiClient* c); 28 | void WSCmdList(WiFiClient* c); 29 | void WSCmdDelete(WiFiClient* c, String s); 30 | void WSCmdFlash(WiFiClient* c, String s); 31 | void WSCmdUpload(WiFiClient* c, String s); 32 | 33 | private: 34 | String DefaultHeader(bool b); 35 | String DefaultFooter(); 36 | void PrintPage(WiFiClient* c, String s); 37 | String GetDirList(); 38 | 39 | int _resetPin = 0; 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /data/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | Arduino Programmer 4 | 5 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
32 |
33 |
 
34 |
Upload
35 |
Cancel
36 |
37 |
38 | 39 |
40 | 41 | 42 | 43 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /data/index.htm.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supperbo/esp_avr_programmer/5af49e0bb25aadbf4c0fef2e692d4e3607dcc912/data/index.htm.gz -------------------------------------------------------------------------------- /esp_avr_programmer.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include "WebServ.h" 3 | 4 | #define RESET_PIN 4 5 | 6 | const char* ssid = "SSID"; 7 | const char* password = "PASSWORD"; 8 | 9 | WiFiServer server(80); 10 | 11 | WebServ webServ(RESET_PIN); 12 | 13 | void setup() 14 | { 15 | WiFi.begin(ssid, password); 16 | 17 | while (WiFi.status() != WL_CONNECTED) 18 | { 19 | delay(500); 20 | } 21 | 22 | server.begin(); 23 | } 24 | 25 | void loop() { 26 | 27 | WiFiClient client = server.available(); 28 | 29 | if (client) { 30 | String line = client.readStringUntil('\r'); 31 | int httpCmd = webServ.GetCommand(line); 32 | String urlParam = webServ.GetUrlParam(line); 33 | 34 | if(httpCmd == webServ.httpCmdIndex) { 35 | webServ.WSCmdIndex(&client); 36 | } else if(httpCmd == webServ.httpCmdDelete) { 37 | webServ.WSCmdDelete(&client, urlParam); 38 | } else if(httpCmd == webServ.httpCmdFlash) { 39 | webServ.WSCmdFlash(&client, urlParam); 40 | } else if(httpCmd == webServ.httpCmdList) { 41 | webServ.WSCmdList(&client); 42 | } else if(httpCmd == webServ.httpCmdUpload) { 43 | webServ.WSCmdUpload(&client, urlParam); 44 | } else { 45 | webServ.WSCmdIndex(&client); 46 | } 47 | 48 | delay(1); 49 | client.stop(); 50 | } 51 | } 52 | 53 | void formatDevice() { 54 | 55 | SPIFFS.begin(); 56 | SPIFFS.format(); 57 | SPIFFS.end(); 58 | 59 | } 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | --------------------------------------------------------------------------------