Guest User

Untitled

a guest
May 29th, 2016
57
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.56 KB | None | 0 0
  1. // I2C to USB Adapter using Arduino
  2. // by Bernhard Kraft <kraftb@think-open.at>
  3.  
  4. /**
  5. * This sketch can get loaded onto an Arduino to use it as USB to I2C Adapter.
  6. * It uses the Wire library. So take a look at the documentation of the Wire
  7. * libarary about the pins being used as SDA/SCL. For most Arduino boards this
  8. * will be analog input pin 4 for SDA and analog input pin 5 for SCL.
  9. *
  10. * On the USB side the default serial link of the Arduino is used. A protocol
  11. * is defined which allows to perform xfer operations on the I2C bus. The
  12. * protocol defines messages which get sent from the PC to the Arduino and
  13. * which get answered by a reply from the Arduino. So the communication is
  14. * bounded in blocks.
  15. *
  16. * It is clear that there needs to be a way to synchronize the PC and the
  17. * Arduino. For this purpose the <ESC> character (0x1B, .27) is used as RESET
  18. * character. Whenever it is received by the Arduino it goes into a defined
  19. * state.
  20. *
  21. * Having only this convention would disallow the <ESC> character in payload
  22. * data. So another character is used to quote/escape the <ESC> character. This
  23. * character is the "\" character (0x5C, .92). To send a literal <ESC> value
  24. * the sequence "0x5C 0xB1" has to get sent (0xB1 is the <ESC> character 0x1B
  25. * with its nibbles exchanged). To send a literal "\" character the sequence
  26. * "0x5C 0xC5" has to get sent. For all other combinations of "0x5C" with
  27. * another character an error is generated (except for "0x5C 0x1B" which is an
  28. * error being signaled after the "\").
  29. *
  30. * When an error is encountered the Arduino sends an <ESC> character (0x1B, not
  31. * the escape sequence) and stops further processing of serial data until it
  32. * gets reset by a <ESC> character from the PC.
  33. *
  34. * The exception mentioned above for the sequence "0x5C 0x1B" results from
  35. * the fact, that 0x1B is the RESET character. So it is possible to cause
  36. * a RESET when a quote character has already been sent.
  37. */
  38.  
  39. #include <Wire.h>
  40.  
  41. /**
  42. * These function signatures are necessary so the file can get compiled
  43. * with the commandline arduino-mk package (apt-get install arduino-mk)
  44. */
  45. void handleReceivedData(byte data);
  46. void escapeSendData(byte data);
  47. void initAdapter();
  48. void handleCommand(byte command);
  49. void escapeSendData(byte data);
  50. void handleReceivedSequence(byte data);
  51. void handleData(byte data);
  52. void handleWireRead();
  53. void handleIdent();
  54.  
  55. /**
  56. * Can't access error register of TWI/Wire library. Thus no errors
  57. * can get recognized for Wire.requestFrom()
  58. */
  59. // extern uint8_t twi_error;
  60.  
  61.  
  62. #define CMD_I2C_ADDRESS 'A'
  63. #define CMD_I2C_LENGTH 'L'
  64. #define CMD_I2C_WRITE_RESTART 'w'
  65. #define CMD_I2C_WRITE 'W'
  66. #define CMD_I2C_READ_RESTART 'r'
  67. #define CMD_I2C_READ 'R'
  68. #define CMD_GET_STATE 'S'
  69. #define CMD_GET_ERROR 'E'
  70. #define CMD_GET_IDENT 'I'
  71. #define CMD_GET_ADDRESS 'a'
  72. #define CMD_GET_LENGTH 'l'
  73.  
  74. #define STATE_INIT 0x00
  75. #define STATE_ERROR 0x01
  76. #define STATE_ADDRESS 0x02
  77. #define STATE_LENGTH 0x03
  78. #define STATE_WRITE 0x05
  79.  
  80. #define CHAR_RESET 0x1B // It is somehow misleading that <ESC> is used for RESET
  81. #define CHAR_ESCAPE 0x5C // And "\" is the escape character.
  82.  
  83. #define CHAR_ESCAPED_RESET 0xB1
  84. #define CHAR_ESCAPED_ESCAPE 0xC5
  85.  
  86. #define ERROR_NONE 'N'
  87. #define ERROR_UNESCAPE 'U'
  88. #define ERROR_LENGTH 'L'
  89. #define ERROR_READ 'R'
  90. #define ERROR_WRITEDATA 'W'
  91. #define ERROR_SENDDATA 'S'
  92.  
  93.  
  94. byte state = STATE_INIT;
  95. byte address = 0;
  96. byte length = 0;
  97. byte error = 0;
  98. boolean restart = false;
  99. char data = 0;
  100. boolean escape = false;
  101.  
  102. String ident = "Arduino I2C-to-USB 1.0";
  103.  
  104. void setup() {
  105. // initialize the serial communication:
  106. Serial.begin(9600);
  107. Wire.begin();
  108. initAdapter();
  109. }
  110.  
  111. void initAdapter() {
  112. // End an eventually ongoing transmission
  113. Wire.endTransmission();
  114.  
  115. state = STATE_INIT;
  116. address = 0;
  117. length = 0;
  118. error = ERROR_NONE;
  119. restart = false;
  120. data = 0;
  121. escape = false;
  122. }
  123.  
  124. void loop() {
  125. while (!Serial) {
  126. // wait for serial port to connect. Needed for Leonardo only
  127. // The state will be "INIT" upon connecting the serial.
  128. initAdapter();
  129. }
  130.  
  131. if (Serial.available()) {
  132.  
  133. if (state == STATE_ERROR) {
  134. // Signal the PC an error
  135. Serial.write(CHAR_RESET);
  136. }
  137.  
  138. // Read data from serial port
  139. data = Serial.read();
  140.  
  141. if (data == CHAR_RESET) {
  142. // When the RESET character has been received cause a reset
  143. initAdapter();
  144. } else {
  145. // Every other character gets passed to "handleReceivedData"
  146. // which will take care about unescaping.
  147. handleReceivedSequence(data);
  148. }
  149.  
  150. if (state == STATE_ERROR) {
  151. // Signal the PC an error
  152. Serial.write(CHAR_RESET);
  153. }
  154. }
  155. }
  156.  
  157.  
  158. /**
  159. * This function handles a passed data byte according to the current state
  160. *
  161. * @param byte data: The received data byte
  162. * @return void;
  163. */
  164. void handleData(byte data) {
  165. if (state == STATE_INIT) {
  166. // The first received byte designates the command
  167. handleCommand(data);
  168. } else if (state == STATE_ADDRESS) {
  169. // In state ADDRESS the passed byte denotes the address upon
  170. // which further commands will act.
  171. address = data;
  172. state = STATE_INIT;
  173. } else if (state == STATE_LENGTH) {
  174. // The LENGTH command defines the number of bytes which
  175. // should get read/written
  176. if (data > BUFFER_LENGTH) {
  177. state = STATE_ERROR;
  178. error = ERROR_LENGTH;
  179. } else {
  180. length = data;
  181. state = STATE_INIT;
  182. }
  183. } else if (state == STATE_WRITE) {
  184. // When in WRITE state the passed value is a data byte which should
  185. // get sent. Pass on as many bytes as specified by a previous LENGTH
  186. // command. Then send it out on the I2C port.
  187. if (length) {
  188. if (Wire.write(data) == 0) {
  189. state = STATE_ERROR;
  190. error = ERROR_WRITEDATA;
  191. return;
  192. }
  193. length--;
  194. }
  195. if (length == 0) {
  196. if (Wire.endTransmission(restart ? false : true) != 0) {
  197. state = STATE_ERROR;
  198. error = ERROR_SENDDATA;
  199. return;
  200. }
  201. restart = false;
  202. state = STATE_INIT;
  203. }
  204. }
  205. }
  206.  
  207. /**
  208. * This function handles a passed command
  209. *
  210. * @param byte command: The command which should get handled
  211. * @return void
  212. */
  213. void handleCommand(byte command) {
  214. switch (command) {
  215.  
  216. case CMD_I2C_ADDRESS:
  217. state = STATE_ADDRESS;
  218. break;
  219.  
  220. case CMD_I2C_LENGTH:
  221. state = STATE_LENGTH;
  222. break;
  223.  
  224. case CMD_I2C_WRITE_RESTART:
  225. restart = true;
  226. case CMD_I2C_WRITE:
  227. Wire.beginTransmission(address);
  228. state = STATE_WRITE;
  229. break;
  230.  
  231. case CMD_I2C_READ_RESTART:
  232. restart = true;
  233. case CMD_I2C_READ:
  234. handleWireRead();
  235. break;
  236.  
  237. case CMD_GET_ADDRESS:
  238. escapeSendData(address);
  239. break;
  240.  
  241. case CMD_GET_LENGTH:
  242. escapeSendData(length);
  243. break;
  244.  
  245. case CMD_GET_STATE:
  246. escapeSendData(state);
  247. break;
  248.  
  249. case CMD_GET_ERROR:
  250. escapeSendData(error);
  251. break;
  252.  
  253. case CMD_GET_IDENT:
  254. handleIdent();
  255. break;
  256.  
  257. }
  258. }
  259.  
  260. void handleIdent() {
  261. int len = ident.length();
  262. char buf[len+1];
  263. ident.toCharArray(buf, len+1);
  264. escapeSendData(len);
  265. // We can use "Serial.write" here because we know the IDENT string
  266. // doesn't contain any characters which would have to get escaped.
  267. Serial.write((uint8_t*)buf, len);
  268. }
  269.  
  270. void handleWireRead() {
  271. Wire.requestFrom((uint8_t)address, (uint8_t)length, (uint8_t)(restart ? 0 : 1));
  272. restart = false;
  273.  
  274. byte a = Wire.available();
  275. escapeSendData(a);
  276. if (a != 0) {
  277. byte r = 0;
  278. for (byte i = 0; i < a; i++) {
  279. r = Wire.read();
  280. escapeSendData(r);
  281. }
  282. }
  283. if (Wire.available() != 0) {
  284. state = STATE_ERROR;
  285. } else {
  286. state = STATE_INIT;
  287. }
  288. }
  289.  
  290. /**
  291. * This function handles the plain received data bytes.
  292. * If it receives the <ESC> character it resets the state machine to INIT state.
  293. * It handles the "\" escape sequence and calls "handleData" for the unescaped
  294. * data having been received.
  295. *
  296. * @param byte data: The received data byte
  297. * @return void
  298. */
  299. void handleReceivedSequence(byte data) {
  300. if (escape) {
  301. escape = false;
  302. switch (data) {
  303. case CHAR_ESCAPED_ESCAPE: // Will cause a "\" (ESCAPE) to get added to the buffer
  304. handleData(CHAR_ESCAPE);
  305. break;
  306.  
  307. case CHAR_ESCAPED_RESET: // Will cause a <ESC> (RESET) to get added to the buffer
  308. handleData(CHAR_RESET);
  309. break;
  310.  
  311. default:
  312. // Every other character causes an error while being in an escape sequence
  313. state = STATE_ERROR;
  314. error = ERROR_UNESCAPE;
  315. break;
  316. }
  317. } else {
  318. if (data == CHAR_ESCAPE) {
  319. escape = true;
  320. } else {
  321. handleData(data);
  322. }
  323. }
  324. }
  325.  
  326. /**
  327. * This function sends the passed byte. It escapes special characters.
  328. *
  329. * @param byte data: The data byte which should get sent.
  330. * @return void
  331. */
  332. void escapeSendData(byte data) {
  333. if (data == CHAR_ESCAPE) {
  334. Serial.write(CHAR_ESCAPE);
  335. Serial.write(CHAR_ESCAPED_ESCAPE);
  336. } else if (data == CHAR_RESET) {
  337. Serial.write(CHAR_ESCAPE);
  338. Serial.write(CHAR_ESCAPED_RESET);
  339. } else {
  340. Serial.write(data);
  341. }
  342. }
Add Comment
Please, Sign In to add comment