flasher.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. "use strict";
  2. var SerialPort = require("serialport").SerialPort;
  3. var bufferpack = require("bufferpack");
  4. var slip = require("slip");
  5. // ../esptool.py --port /dev/cu.SLAB_USBtoUART --baud 115200 \
  6. // write_flash --flash_freq 80m --flash_mode qio --flash_size 32m \
  7. // 0x0000 "boot_v1.4(b1).bin" 0x1000 espruino_esp8266_user1.bin \
  8. // 0x3FC000 esp_init_data_default.bin 0x3FE000 blank.bin
  9. // Marriage of ESPTOOL-CK and ESPTOOL.py
  10. const formats = {
  11. bootloader_packet_header: "<B(direction)B(command)H(size)I(checksum)"
  12. };
  13. const commands = {
  14. CMD0: 0x00,
  15. CMD1: 0x01,
  16. FLASH_DOWNLOAD_BEGIN: 0x02,
  17. FLASH_DOWNLOAD_DATA: 0x03,
  18. FLASH_DOWNLOAD_DONE: 0x04,
  19. RAM_DOWNLOAD_BEGIN: 0x05,
  20. RAM_DOWNLOAD_END: 0x06,
  21. RAM_DOWNLOAD_DATA: 0x07,
  22. SYNC_FRAME: 0x08,
  23. WRITE_REGISTER: 0x09,
  24. READ_REGISTER: 0x0A,
  25. SET_FLASH_PARAMS: 0x0B,
  26. NO_COMMAND: 0xFF
  27. };
  28. const SYNC_FRAME = new Buffer("\x07\x07\x12\x20" + "\x55".repeat(32));
  29. function slipReadParser(emitter, buffer) {
  30. // This is the pyramid of doom right?
  31. var decoder = new slip.Decoder({
  32. onMessage: (msg) => {
  33. emitter.emit('data', msg);
  34. }
  35. });
  36. debug("Got buffer", buffer.length);
  37. decoder.decode(buffer);
  38. }
  39. var debug = function() {};
  40. function delay(time) {
  41. return new Promise((resolve) => {
  42. debug("Sleepy time", time);
  43. setTimeout(resolve, time);
  44. });
  45. }
  46. class EspBoard {
  47. constructor(port) {
  48. this.port = port;
  49. }
  50. resetIntoBootLoader() {
  51. // RTS - Request To Send
  52. // DTR - Data Terminal Ready
  53. return new Promise((resolve, reject) => {
  54. this.port.set({
  55. rts: true,
  56. dtr: true
  57. }, function(error, result) {
  58. if (error) {
  59. reject(error);
  60. }
  61. resolve(result);
  62. });
  63. }).then(() => {
  64. return delay(5);
  65. }).then(() => {
  66. this.port.set({rts: false}, (error, result) => {
  67. debug("Second go", error, result);
  68. });
  69. }).then(() => {
  70. return delay(250);
  71. }).then(() => {
  72. this.port.set({dtr: false}, (error, result) => {
  73. debug("Third go", error, result);
  74. });
  75. });
  76. }
  77. }
  78. class EspComm {
  79. constructor(config) {
  80. this.port = new SerialPort(config.portName, {
  81. baudRate: config.baudRate,
  82. parser: slipReadParser
  83. }, false);
  84. var BoardFactory = config.BoardFactory ? config.BoardFactory : EspBoard;
  85. this.board = new BoardFactory(this.port);
  86. if (config.debug) {
  87. debug = config.debug;
  88. }
  89. this.isOpen = false;
  90. this.config = config;
  91. }
  92. open() {
  93. return new Promise((resolve, reject) => {
  94. this.port.open((error) => {
  95. debug("Opening port...", this.port);
  96. if (error) {
  97. reject(error);
  98. } else {
  99. resolve();
  100. }
  101. });
  102. }).then(() => {
  103. return this.sync();
  104. });
  105. }
  106. close() {
  107. this.port.close();
  108. this.isOpen = false;
  109. }
  110. calculateChecksum(data) {
  111. var result = 0xEF;
  112. for (var i = 0; i < data.length; i++) {
  113. result ^= data[i];
  114. }
  115. return result;
  116. }
  117. sync() {
  118. return this.board.resetIntoBootLoader()
  119. .then(() => {
  120. return new Promise((resolve, reject) => {
  121. this.port.flush((error) => {
  122. if (error) {
  123. reject(error);
  124. }
  125. resolve();
  126. });
  127. }).then(() => {
  128. return this.sendCommand(commands.SYNC_FRAME, SYNC_FRAME)
  129. .then((result) => {
  130. // There is some magic here
  131. debug("Should we retry 7 times??");
  132. });
  133. });
  134. });
  135. }
  136. // TODO:csd - How to make the commands pretty?
  137. // https://github.com/themadinventor/esptool/blob/master/esptool.py#L108
  138. // https://github.com/igrr/esptool-ck/blob/master/espcomm/espcomm.c#L103
  139. sendCommand(command, data) {
  140. // ???:csd - Is this how you do OO anymore?
  141. var port = this.port;
  142. return new Promise((resolve, reject) => {
  143. var sendHeader = bufferpack.pack(formats.bootloader_packet_header, [0x00, command, data.length, this.calculateChecksum(data)]);
  144. port.write(slip.encode(sendHeader), (err, result) => {
  145. debug("Sending header", err, result);
  146. });
  147. port.write(slip.encode(data), (err, result) => {
  148. debug("Sending budy", err, result);
  149. });
  150. port.on('data', (buffer) => {
  151. debug("Port got data", buffer);
  152. var receiveHeader = bufferpack.unpack(formats.bootloader_packet_header, buffer.readInt8(0));
  153. // FIXME:csd - Sanity check here regarding direction???
  154. resolve({
  155. header: receiveHeader,
  156. // Result follows the header
  157. data: buffer.slice(8)
  158. });
  159. });
  160. });
  161. }
  162. }
  163. module.exports = EspComm;