Leading the way to the Future
Welcome Guest, Login!
04 Aug 2010 01:42:14 pm by KermMartian Arduino to TI Calculator Linking Routines Quote
I wrote these a few months ago for my Arduino to TI-84+ linking project; I thought they might be useful to others. You basically call resetLines() at the beginning of the program, then par_put and par_get as necessary. Note that these of course use the TI-OS protocol, so you can use them for remote control, program injection, screenshot fetching, and all that fun stuff.


Link


Code:
void resetLines();
static int par_put(uint8_t *data, uint32_t len);
static int par_get(uint8_t *data, uint32_t len);
#define TIwhite TIring
#define TIred TItip
#define ERR_READ_TIMEOUT 1000
#define ERR_WRITE_TIMEOUT 2000
#define TIMEOUT 4000
#define GET_ENTER_TIMEOUT 30000

void resetLines() {
   pinMode(TIring, INPUT);           // set pin to input
   digitalWrite(TIring, HIGH);       // turn on pullup resistors
   pinMode(TItip, INPUT);            // set pin to input
   digitalWrite(TItip, HIGH);        // turn on pullup resistors
}
static int par_put(uint8_t *data, uint32_t len) {
   int bit;
   int i, j;
   long previousMillis = 0;
   uint8_t byte;   

   for(j=0;j<len;j++) {
      byte = data[j];
      for (bit = 0; bit < 8; bit++) {
         previousMillis = 0;
         while ((digitalRead(TIring)<<1 | digitalRead(TItip)) != 0x03) {
            if (previousMillis++ > TIMEOUT)
               return ERR_WRITE_TIMEOUT+j+100*bit;
         };
         if (byte & 1) {
            pinMode(TIring,OUTPUT);
            digitalWrite(TIring,LOW);
            previousMillis = 0;
            while (digitalRead(TItip) == HIGH) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_WRITE_TIMEOUT+10+j+100*bit;
            };

            resetLines();
            previousMillis = 0;
            while (digitalRead(TItip) == LOW) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_WRITE_TIMEOUT+20+j+100*bit;
            };
         } else {
            pinMode(TItip,OUTPUT);
            digitalWrite(TItip,LOW);      //should already be set because of the pullup resistor register
            previousMillis = 0;
            while (digitalRead(TIring) == HIGH) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_WRITE_TIMEOUT+30+j+100*bit;
            };

            resetLines();
            previousMillis = 0;
            while (digitalRead(TIring) == LOW) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_WRITE_TIMEOUT+40+j+100*bit;
            };
         }
         byte >>= 1;
      }
      //delayMicroseconds(6);
   }
   return 0;
}

static int par_get(uint8_t *data, uint32_t len) {
   int bit;
   int i, j;
   long previousMillis = 0;

   for(j = 0; j < len; j++) {
      uint8_t v, byteout = 0;
      for (bit = 0; bit < 8; bit++) {
         previousMillis = 0;
         while ((v = (digitalRead(TIring)<<1 | digitalRead(TItip))) == 0x03) {
            if (previousMillis++ > GET_ENTER_TIMEOUT)
               return ERR_READ_TIMEOUT+j+100*bit;
         }
         if (v == 0x01) {
            byteout = (byteout >> 1) | 0x80;
            pinMode(TItip,OUTPUT);
            digitalWrite(TItip,LOW);      //should already be set because of the pullup resistor register
            previousMillis = 0;
            while (digitalRead(TIring) == LOW) {            //wait for the other one to go low
               if (previousMillis++ > TIMEOUT)
                  return ERR_READ_TIMEOUT+10+j+100*bit;
            }
            //pinMode(TIring,OUTPUT);
            digitalWrite(TIring,HIGH);
         } else {
            byteout = (byteout >> 1) & 0x7F;
            pinMode(TIring,OUTPUT);
            digitalWrite(TIring,LOW);      //should already be set because of the pullup resistor register
            previousMillis = 0;
            while (digitalRead(TItip) == LOW) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_READ_TIMEOUT+20+j+100*bit;
            }
            //pinMode(TItip,OUTPUT);
            digitalWrite(TItip,HIGH);
         }
         pinMode(TIring, INPUT);           // set pin to input
         digitalWrite(TIring, HIGH);       // turn on pullup resistors
         pinMode(TItip, INPUT);            // set pin to input
         digitalWrite(TItip, HIGH);        // turn on pullup resistors
      }
      data[j] = byteout;
      //delayMicroseconds(6);
   }
   return 0;
}
04 Feb 2012 12:06:38 pm by CharlieMAC Quote
AWESOME!!
04 Feb 2012 01:48:08 pm by merthsoft Quote
Would you be able to explain what the code calc-side should look like?
05 Feb 2012 08:53:00 am by KermMartian Quote
merthsoft wrote:
Would you be able to explain what the code calc-side should look like?
An excellent question. First of all, this can be used out-of-the-box for things like remote-controlling a calculator with injected keypresses, taking remote screenshots, and just about everything on this page from the File Format/Link Protocol Guide. Of course, if you're using it to chat with a user program, I like to recommend that you use CALCnet instead, but with the Rec1stByte and SendAByte bcalls, you can indeed interface with these routines. Both routines use the accumulator for the byte to send or receive and use the TI-OS error system if something goes wrong.
05 Feb 2012 11:47:37 am by merthsoft Quote
Ah, right. It's the whole silent linking thing, right? So you can just send it commands and the OS will handle things. Good to know about the Rec1stByte and SendAByte bcalls, though. Thanks!
06 Feb 2012 07:54:14 pm by CharlieMAC Quote
Is there any difference between the TI-83 Linking protocol and TI-89t Linking protocol? I'm asking because I'd like to control a couple of servos using my calculator, which is the only I have in my possession.
07 Feb 2012 08:37:11 am by KermMartian Quote
CharlieMAC wrote:
Is there any difference between the TI-83 Linking protocol and TI-89t Linking protocol? I'm asking because I'd like to control a couple of servos using my calculator, which is the only I have in my possession.
The bottom two or three layers (if you subscribe to the OSI model) are the same: bytes and bits and the physical connection are the same, other than some voltage-leveling quirks.
13 Apr 2012 04:10:57 pm by bartjakobs Quote
Hi.
I'm not very good at electronics and stuff so...
How should I connect the calculator? I have some simple 2,5 mm to 3,5 mm audio cables (which are usable to connect two TI-83 calcs), but I don't have any official cables.
What wires do I have to connect to the Arduino?
Also, How can I send a command?

I tried this, but it's probably wrong.

uint8_t command [4] = {0x23,0x87,0xA6,0x00};
par_put(command, 4);
13 Apr 2012 06:35:22 pm by KermMartian Quote
bartjakobs wrote:
Hi.
I'm not very good at electronics and stuff so...
How should I connect the calculator? I have some simple 2,5 mm to 3,5 mm audio cables (which are usable to connect two TI-83 calcs), but I don't have any official cables.
What wires do I have to connect to the Arduino?
Also, How can I send a command?

I tried this, but it's probably wrong.

uint8_t command [4] = {0x23,0x87,0xA6,0x00};
par_put(command, 4);
Welcome to Cemetech! You should Introduce Yourself if you get a change. That code is exactly right, as long as you have resetLines() at the beginning of your program somewhere. Ground should go to ground, and the two signal lines should go to GPIO pins on the Arduino. You get to define that yourself:

Code:
#define TIring <pin number>
#define TItip <pin number>
13 Apr 2012 09:53:26 pm by adept Quote
Thank you SO much for finally releasing this code. So much more helpful this way. But I can't just use the Omnicalc linking commands... I'm reading your other links that your pointed out to see how my particular calc-side needs to be. Also, I'll need to add in some stuff that interprets the html for me, but this is going to help a lot!
14 Apr 2012 09:16:06 am by KermMartian Quote
I released this over a year and a half ago; look at the date on the first post. What are you talking about with HTML? Why not just use CALCnet and Gossamer?
29 Aug 2012 10:50:16 am by lyron Quote
So if i for example wanted to send the keypress "A" to my TI, would this work:

Code:

uint8_t A [4] = {0x23,0x15,0x41,0x00};
par_put(A, 4);

?
29 Aug 2012 11:15:32 am by KermMartian Quote
lyron wrote:
So if i for example wanted to send the keypress "A" to my TI, would this work:

Code:

uint8_t A [4] = {0x23,0x15,0x41,0x00};
par_put(A, 4);

?
Welcome to Cemetech! A capital 'A' is 0x9A instead of 0x41 according to this list of keycodes, but other than that, yes. By the way, be sure to Introduce Yourself when you get a chance.
31 Aug 2012 09:00:46 am by lyron Quote
KermMartian wrote:

A capital 'A' is 0x9A instead of 0x41 according to this list of keycodes, but other than that, yes.


Okay, now i'm using this code:
Code:
/*
TI 84+ control script
By Niek Blankers

With thanks to Kerm Martian for the par_get and par_put code
*/

void resetLines();
static int par_put(uint8_t *data, uint32_t len);
static int par_get(uint8_t *data, uint32_t len);
#define TIring 7 //White
#define TItip 8 //Red
#define ERR_READ_TIMEOUT 1000
#define ERR_WRITE_TIMEOUT 2000
#define TIMEOUT 4000
#define GET_ENTER_TIMEOUT 30000

void setup(){
resetLines();

}

void loop(){
uint8_t A [4] = {0x23,0x15,0x41,0x00};  //packet for sending capital A
par_put(A, 4); //send the packet
delay(2000);
}



//TI LINK PROTOCOL CODE
void resetLines() {
   pinMode(TIring, INPUT);           // set pin to input
   digitalWrite(TIring, HIGH);       // turn on pullup resistors
   pinMode(TItip, INPUT);            // set pin to input
   digitalWrite(TItip, HIGH);        // turn on pullup resistors
}
static int par_put(uint8_t *data, uint32_t len) {
   int bit;
   int i, j;
   long previousMillis = 0;
   uint8_t byte;   

   for(j=0;j<len;j++) {
      byte = data[j];
      for (bit = 0; bit < 8; bit++) {
         previousMillis = 0;
         while ((digitalRead(TIring)<<1 | digitalRead(TItip)) != 0x03) {
            if (previousMillis++ > TIMEOUT)
               return ERR_WRITE_TIMEOUT+j+100*bit;
         };
         if (byte & 1) {
            pinMode(TIring,OUTPUT);
            digitalWrite(TIring,LOW);
            previousMillis = 0;
            while (digitalRead(TItip) == HIGH) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_WRITE_TIMEOUT+10+j+100*bit;
            };

            resetLines();
            previousMillis = 0;
            while (digitalRead(TItip) == LOW) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_WRITE_TIMEOUT+20+j+100*bit;
            };
         } else {
            pinMode(TItip,OUTPUT);
            digitalWrite(TItip,LOW);      //should already be set because of the pullup resistor register
            previousMillis = 0;
            while (digitalRead(TIring) == HIGH) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_WRITE_TIMEOUT+30+j+100*bit;
            };

            resetLines();
            previousMillis = 0;
            while (digitalRead(TIring) == LOW) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_WRITE_TIMEOUT+40+j+100*bit;
            };
         }
         byte >>= 1;
      }
      //delayMicroseconds(6);
   }
   return 0;
}

static int par_get(uint8_t *data, uint32_t len) {
   int bit;
   int i, j;
   long previousMillis = 0;

   for(j = 0; j < len; j++) {
      uint8_t v, byteout = 0;
      for (bit = 0; bit < 8; bit++) {
         previousMillis = 0;
         while ((v = (digitalRead(TIring)<<1 | digitalRead(TItip))) == 0x03) {
            if (previousMillis++ > GET_ENTER_TIMEOUT)
               return ERR_READ_TIMEOUT+j+100*bit;
         }
         if (v == 0x01) {
            byteout = (byteout >> 1) | 0x80;
            pinMode(TItip,OUTPUT);
            digitalWrite(TItip,LOW);      //should already be set because of the pullup resistor register
            previousMillis = 0;
            while (digitalRead(TIring) == LOW) {            //wait for the other one to go low
               if (previousMillis++ > TIMEOUT)
                  return ERR_READ_TIMEOUT+10+j+100*bit;
            }
            //pinMode(TIring,OUTPUT);
            digitalWrite(TIring,HIGH);
         } else {
            byteout = (byteout >> 1) & 0x7F;
            pinMode(TIring,OUTPUT);
            digitalWrite(TIring,LOW);      //should already be set because of the pullup resistor register
            previousMillis = 0;
            while (digitalRead(TItip) == LOW) {
               if (previousMillis++ > TIMEOUT)
                  return ERR_READ_TIMEOUT+20+j+100*bit;
            }
            //pinMode(TItip,OUTPUT);
            digitalWrite(TItip,HIGH);
         }
         pinMode(TIring, INPUT);           // set pin to input
         digitalWrite(TIring, HIGH);       // turn on pullup resistors
         pinMode(TItip, INPUT);            // set pin to input
         digitalWrite(TItip, HIGH);        // turn on pullup resistors
      }
      data[j] = byteout;
      //delayMicroseconds(6);
   }
   return 0;
}


And hooked everything up but it doesn't do anything Sad [/code]
31 Aug 2012 12:09:27 pm by KermMartian Quote
How do you have the calculator's link port connected to the Arduino?
31 Aug 2012 12:21:32 pm by lyron Quote
KermMartian wrote:
How do you have the calculator's link port connected to the Arduino?


I purchased a 2.5mm jack and soldered some wires to the correct terminals.
31 Aug 2012 12:22:41 pm by KermMartian Quote
lyron wrote:
KermMartian wrote:
How do you have the calculator's link port connected to the Arduino?


I purchased a 2.5mm jack and soldered some wires to the correct terminals.
And it's a stereo (3-conductor) jack? Are you sure you have the two signal pins on the right Arduino pins, and the ground to ground? Does the calculator's cursor change at all?
31 Aug 2012 12:34:47 pm by lyron Quote
KermMartian wrote:
lyron wrote:
KermMartian wrote:
How do you have the calculator's link port connected to the Arduino?


I purchased a 2.5mm jack and soldered some wires to the correct terminals.
And it's a stereo (3-conductor) jack? Are you sure you have the two signal pins on the right Arduino pins, and the ground to ground? Does the calculator's cursor change at all?


Here's a photo:


The cursor does not move at all.

Tari: resized huge image
01 Sep 2012 05:12:33 am by lyron Quote
Could anybody else try my code?
01 Sep 2012 08:18:50 am by KermMartian Quote
lyron wrote:
Could anybody else try my code?
I can try it later; I need to get my Arduino from my office. Make sure that the plug is flush into your calculator's port, by the way. Do you have a TI-83+/SE or a TI-84+/SE? If the latter, regular 2.5mm plugs sometimes have too wide of a flange (the metal disc below the plug) to fully mate into the socket.