/* ========= Pushbutton 3 (GS) / NC 9 | * * | 8 Ground Gm Ctrl 1 (Stick-1 Y) 10 | * * | 7 Gm Ctrl 2 (Stick-2 X) Gm Ctrl 3 (Stick-2 Y) 11 | * * | 6 Gm Ctrl 0 (Stick-1 X) Annunciator 3 12 | * * | 5 /$C040 Strobe Annunciator 2 13 | * * | 4 Pushbutton 2 / A7 / $c063 /SHIFT Annunciator 1 14 | * * | 3 Pushbutton 1 / A8 / $c062 Annunciator 0 15 | * * | 2 Pushbutton 0 / A10 / $c061 No Connection 16 | * * | 1 +5V ===| |=== ^ SETAN0 = $C058 ;Set annunciator-0 output to 0 CLRAN0 = $C059 ;Set annunciator-0 output to 1 SETAN1 = $C05A ;Set annunciator-1 output to 0 CLRAN1 = $C05B ;Set annunciator-1 output to 1 SETAN2 = $C05C ;Set annunciator-2 output to 0 CLRAN2 = $C05D ;Set annunciator-2 output to 1 SETAN3 = $C05E ;Set annunciator-3 output to 0 CLRAN3 = $C05F ;Set annunciator-3 output to 1 ..xx xx.. 3C .x.. ..x. 42 x.x. .x.x a5 x.x. .x.x a5 x... ...x 81 x.x. .x.x a5 .x.x x.x. 5a ..xx xx.. 3c */ // I2C #define SDA_PORT PORTB #define SDA_PIN 5 #define SCL_PORT PORTB #define SCL_PIN 6 #define I2C_TIMEOUT 100 #define I2C_NOINTERRUPT 0 #define I2C_SLOWMODE 1 #define FAC 1 #define I2C_CPUFREQ (F_CPU/FAC) #include #include // Adafruit I2C LED backpacks #define HT16K33_BLINK_CMD 0x80 #define HT16K33_BLINK_DISPLAYON 0x01 #define HT16K33_BLINK_OFF 0 #define HT16K33_CMD_BRIGHTNESS 0xE0 uint8_t i2c_addr = 0xe0;//0x70; // UART/RS232 #define HWSERIAL Serial1 int counter = 0; int delayLoop = 0; bool sendingBit = 0; bool FlipFlop = 0; volatile long currentMicros = 0; volatile long lastMicros = 0; volatile int receivedBit; volatile int delayMillis; volatile long TIMEOUTCLOCK; long TIMESINCELASTBYTE; char receivedByte; byte returnByte; int changeCount = 0; int bitCount = 0; int byteArray[8]; //char stack[256]; char inputBuffer[256]; byte outputBuffer[256]; //byte USBSERIALBuffer[256]; //byte HWSERIALBuffer[256]; //int HWSERIALBufferLength = 0; byte bufferLength[1]; /*byte I2CBuffer[8] = { B00000000, B00000000, B10101110, B01101010, B10101110 }; byte I2CBuffer[8] = { B01111010, B10111000, B00000000, B10111000, B10111101, B10101110, B01101010, B10101110 };*/ byte I2CBuffer[8] = { B01010101, /* bit 7--0 */ B10101010, B01010101, B10101010, B01010101, B10101010, B01010101, B10101010 }; /* * * * * 1 --- 6 | 7 | 2 --- 5 | 4 | 3 --- . 0 colon is I2CBuffer[2], FF on 00 off */ int LED_ON = 1; int messageLength; int stackPointer; int inputPointer; int outputPointer; unsigned long baud = 9600; const int reset_pin = 4; byte ESPByte; const int LED_PIN = 11; int RGB_RED = 10; int RGB_GREEN = 4; int RGB_BLUE = 9; int ANN0_PIN = 5; int ANN1_PIN = 6; int ANN2_PIN = 0; int ANN3_PIN = 1; char PB0_PIN = 13; char PB1_PIN = 12; char PB2_PIN = 21; int IO_MODE; int NUM_MODES = 9; int functionLength = 0; char currentFunction; int functionArray[] = { B00000001, //0 $01 RGB LED, 3 bytes B00000010, //1 $02 RGB LED, 1 byte (white intensity) B00000100, //2 $04 WRITE TO INTERNAL BUFFER, 1 byte (message length), message (0-255 bytes). B00001000, //3 $08 WRITE TO SERIAL, 1 byte (message length), message (0-255 bytes). B00010000, //4 $10 WRITE TO I2C bus 8x8 LED Matrix, 7-segment, etc, 1 byte (message length), message (0-255 bytes). B00100000, //5 $20 SPI (not yet implemented), READPIN, retreive analogRead from pin N B01000000, //6 $40 QUERY BUFFER, 0 bytes. SEND 1 byte response with buffer length B10000000, //7 $80 SEND BUFFER CONTENT, 1 byte (buffer to send). Ready to receive, expecting 0-255 bytes. B00000000 }; // $00 DEBUG mode. writes all subsequent bytes received to Serial Out. Sends one byte at a time from buffer to Apple on LATCH. //DEBUG is active until the AVR is reset or input timeout is reached. void setup() { pinMode(ANN2_PIN, INPUT); // annunciator 2 pinMode(ANN3_PIN, INPUT); // annunciator 3 pinMode(ANN0_PIN, INPUT_PULLUP); pinMode(ANN1_PIN, INPUT_PULLUP); pinMode(RGB_RED, OUTPUT); pinMode(RGB_GREEN, OUTPUT); pinMode(RGB_BLUE, OUTPUT); pinMode(PB1_PIN, OUTPUT); // PB1 / solid apple pinMode(PB0_PIN, OUTPUT); // PB0 / open apple digitalWrite(PB0_PIN, LOW); // RTS digitalWrite(PB1_PIN, LOW); // Bits out digitalWrite(PB2_PIN, HIGH); // SHIFT is active LOW pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); /* pinMode(14, OUTPUT); digitalWrite(14, HIGH); pinMode(15, OUTPUT); digitalWrite(15, HIGH); pinMode(16, OUTPUT); digitalWrite(16, HIGH); pinMode(17, OUTPUT); digitalWrite(17, HIGH); pinMode(18, OUTPUT); digitalWrite(18, HIGH); pinMode(19, OUTPUT); digitalWrite(19, HIGH); pinMode(20, OUTPUT); digitalWrite(20, HIGH); pinMode(21, OUTPUT); digitalWrite(21, HIGH); */ Serial.begin(baud); // to ESP, other serial devices // HWSERIAL.begin(baud); enablePinInterrupt(ANN2_PIN); // Apple ready to receive attachInterrupt(0, APPLERTS, RISING); // ANNUNCIATOR 0, Apple sending byte attachInterrupt(1, RECEIVINGBITS, CHANGE); // ANNUNCIATOR 1, Apple sending bits setRGBOneShot(200, 200, 200); // blue = ready //LED MATRIX / 7 segment display if (!i2c_init()) { Serial.println("I2C error. SDA or SCL are low"); } if (!i2c_start(i2c_addr) ) { Serial.println("I2C device not found"); } i2c_write(0x21); // turn on oscillator i2c_stop(); // set blinkrate 0 i2c_start(i2c_addr); i2c_write(HT16K33_BLINK_CMD | HT16K33_BLINK_DISPLAYON | (0 << 1)); i2c_stop(); // set brightness to 15/15 i2c_start(i2c_addr); i2c_write(HT16K33_CMD_BRIGHTNESS | 15); i2c_stop(); writeI2C(); // "Good"/"ok" ClearI2C(); } void loop() { TIMESINCELASTBYTE = millis() - TIMEOUTCLOCK; if ( TIMESINCELASTBYTE > 5000 && IO_MODE > -1) { IOMODERESET(TIMESINCELASTBYTE, "timeout"); } int incomingByte; if (Serial.available() > 0) { incomingByte = Serial.read(); HWSERIAL.write(incomingByte); } if (HWSERIAL.available() > 0) { incomingByte = HWSERIAL.read(); bufferLength[0]++; // should be fast enough to get one byte at a time... digitalWrite(PB0_PIN, HIGH); // RTS == UART has bytes to send outputBuffer[bufferLength[0]] = incomingByte; Serial.write(outputBuffer[bufferLength[0]]); } } //loop void IOMODERESET(long resetTime, String reason) { Serial.print("B"); Serial.println(bufferLength[0]); if ( TIMESINCELASTBYTE > 5000) { // double check there's actually been a timeout... functionLength = 0; IO_MODE = -1; // Serial.print("MODE RESET "); Serial.println(reason); // Serial.println(TIMESINCELASTBYTE); setRGBOneShot(200, 200, 200); digitalWrite(PB0_PIN, LOW); // RTS off digitalWrite(PB1_PIN, LOW); // Bits out digitalWrite(PB2_PIN, HIGH); // SHIFT is active LOW digitalWrite(LED_PIN, LOW); // Serial.write(HWSERIALBuffer, 256); } } void enablePinInterrupt(byte pin) { *digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group } ISR (PCINT0_vect) // handle pin change interrupt { //APPLECTS == ANN2 pin rising if (digitalRead(ANN2_PIN) == HIGH) { // ready for queued byte, annunciator 2 HIGH if (IO_MODE == 6) { // sending one-byte buffer length. sendByte(bufferLength[0], 1); functionLength = 0; IO_MODE = -1; // Serial.println(bufferLength[0]); // Serial.println(outputPointer); } else if (outputPointer < bufferLength[0]) { // will always return something when queried, for debugging. // Serial.print(outputPointer); // Serial.println(outputBuffer[outputPointer]); sendByte(outputBuffer[outputPointer], 0); outputPointer++; Serial.print(outputPointer); Serial.print(" == "); Serial.println(bufferLength[0]); if (outputPointer == bufferLength[0]) { // zero padding? // Serial.println("BUFFER RESET"); sendByte(0, 1); outputPointer = 0; bufferLength[0] = 0; } } /* if (outputPointer == bufferLength[0]) { // done sending. RTS LOW digitalWrite(PB0_PIN, LOW); // RTS LOW == no more buffer to send } */ digitalWrite(LED_PIN, HIGH); // set the LED on } else { digitalWrite(LED_PIN, LOW); // set the LED off } } void sendByte (byte byteToSend, bool oneShot) { if (oneShot) { outputPointer = 0; } else { // outputPointer++; } delay(100); // ~7ms digitalWrite(PB1_PIN, HIGH); for (int i = 0; i < 8; i++) { // send 8 bits, 9 transitions int bitToSend = bitRead(byteToSend, 7 - i); if (bitToSend == 1) { delay(80); // ~13ms } delay(20); // ~7ms if ( i % 2 ) { digitalWrite(PB1_PIN, HIGH); } else { digitalWrite(PB1_PIN, LOW); } } delay(100); // ~7ms digitalWrite(PB1_PIN, HIGH); delay(100); // ~7ms digitalWrite(PB1_PIN, LOW); } void APPLERTS() { // signal to start receiving bits from Apple II" bitCount = 0; changeCount = 0; returnByte = B00000000; // Serial.print("RECEIVING. IO MODE: "); // Serial.println(IO_MODE); // Serial.print(" FUNCTION LENGTH: "); // Serial.println(functionLength); } void RECEIVINGBITS() { // ignore short "reset" transitions currentMicros = micros(); if (changeCount % 2 == 1) { // Serial.println(currentMicros - lastMicros); // Serial.print( " = " ); if ((currentMicros - lastMicros) > 70) { receivedBit = 1; //digitalWrite(A0,HIGH); //digitalWrite(A1,LOW); } else { receivedBit = 0; // digitalWrite(A1,HIGH); // digitalWrite(A0,LOW); } // Serial.println(receivedBit); byteArray[7 - bitCount] = receivedBit; //Serial.print(receivedByte); /* if (receivedBit == 0) { returnByte &= ~(0 << (7 - bitCount)); } else { returnByte |= (1 << (7 - bitCount)); } */ /* starts as 00000000 1 or 0 gets added, byte shifted left */ //returnByte = returnByte | receivedBit; // 00000000 to 00000001 //returnByte = returnByte << 1; // 00000001 to 00000010 bitCount++; } changeCount++; if (bitCount == 8) { // got a BYTE // Serial.println(receivedBit); receivedByte = arrayToByte(byteArray, 8); // Serial.print("HEX "); // Serial.println( String(arrayToByte(byteArray,8),HEX) ); // Serial.print("DEC "); //Serial.println("."); digitalWrite(LED_PIN, LOW); // setRGBOneShot(0, 200, 0); PROCESSBYTE( byte(receivedByte) ); TIMEOUTCLOCK = millis(); } lastMicros = currentMicros; } void PROCESSBYTE(byte receivedByte) { // char longstring[80] = "AT+CIPSTART=\"TCP\",\"www.option8llc.com\",80\n\r"; // int rd, wr; String connectString; int readPin; // pin to read for readpin mode byte returnByte; // // Serial.print("P"); // Serial.println(receivedByte); //setRGBOneShot(0, 200, 200); // TIMEOUTCLOCK = millis(); /* if no IO_MODE, wait on a byte from functionArray to start IO. case IO_MODE each mode sets number of bytes required, sets byte countdown "functionLength" subsequent bytes in processbyte fall through to io_mode functionLength = 0, done with IO function, waiting on next switch byte */ if (functionLength == 0) { // waiting on next function // interpret bytes as mode switch, values for (int i = 0; i < NUM_MODES; i++) { // Serial.println(functionArray[i]); if (receivedByte == functionArray[i]) { IO_MODE = i; Serial.print("F"); Serial.println(i); TIMEOUTCLOCK = millis(); break; } else { IO_MODE = -1; } } // Serial.println(IO_MODE); switch (IO_MODE) { case 0: // RGB PIXEL functionLength = 3; // Serial.println("RGB LED - three more bytes"); break; case 1: // RGB PIXEL, white functionLength = 1; // Serial.println("White LED MODE - 1 more byte"); break; case 2: // $04 WRITE TO STACK, 1 byte (message length), message (0-255 bytes) functionLength = -1; stackPointer = 0; break; case 3: // $08 WRITE TO UART SERIAL, 1 byte (message length), message (0-255 bytes) /* long string read available write size wr move wr bytes from longstring to writebuffer write wr bytes to HWSERIAL wait X */ // connectString = "AT+CWLAP\n\r"; // connectString.getBytes(HWSERIALBuffer, 16);//sizeof(connectString)); // char HWSERIALBuffer[messageLength]; // connectString.toCharArray(HWSERIALBuffer, messageLength); // Serial.print("Content length= "); // Serial.println(messageLength); //bufferToHWSERIAL(sizeof(connectString)); // functionLength = -1; // stackPointer = 0; // Serial.println("ESP8266"); // HWSERIAL.println("AT+CWLAP"); //HWSERIAL.availableForWrite(); //HWSERIAL.write("AT+CIPSTART=\"TCP\",\"www.option8llc.com\",80\n\r"); //HWSERIAL.println("AT+CIPSEND=72"); // HWSERIAL.println("GET /KFEST.TXT HTTP/1.1"); // HWSERIAL.println("Host: option8llc.com"); // HWSERIAL.println("Connection: close"); IOMODERESET(TIMESINCELASTBYTE, "serial demo"); // ONE-SHOT break; case 4: // 0x10 write pixels to 8x8 matrix display functionLength = -1; // wait on N bytes for matrix/7seg display // first byte sets message length break; case 5: // 0x20 read pinstate of pin determined by first byte functionLength = 1; Serial.print("READPIN "); break; case 6: // QUERY BUFFER - send 1 byte, buffer length functionLength = 1; Serial.print("RTS "); //Serial.print("SENDING BUFFER LENGTH = "); // Serial.println(bufferLength[0]); //messageLength = 80; // how to get the last byte/length of the buffer ??? //outputBuffer = char("this is a test."); //outputPointer = 0; //sendByte(outputBuffer[0]); //bufferLength[0] = HWSERIALBufferLength; //sendByte(bufferLength[0], 1); break; case 7: // SEND BUFFER - send the bytes from the stack functionLength = 1; Serial.print("RTS "); break; case 8: // DEBUG MODE functionLength = -1; Serial.println("DEBUG MODE."); break; default: Serial.print("BAD MODE."); Serial.println(receivedByte); break; } } else { // waiting on n bytes // Serial.println(functionLength); switch (IO_MODE) { case 0: // RGB LED setRGB(receivedByte, functionLength); functionLength --; break; case 1:// RGB LED WHITE setRGB(receivedByte, 0); functionLength = 0; break; case 2:// read bytes into local buffer //Serial.print(receivedByte); if (functionLength == -1) { // first byte == message length functionLength = receivedByte; Serial.print("I"); Serial.println(functionLength); } else { inputBuffer[stackPointer] = receivedByte; stackPointer++; // Serial.println(stackPointer); if (stackPointer == functionLength) { // last byte for (int i = 0; i < stackPointer; i++) { HWSERIAL.write(inputBuffer[i]); } HWSERIAL.write('\r'); HWSERIAL.write('\n'); IOMODERESET(TIMESINCELASTBYTE, "readbytes"); // SEEMS TO GET MISSED? IO_MODE = -1; functionLength = 0; } } break; case 3:// read bytes and send to UART (e.g. ESP8266) // handled in interrupts break; case 4: // read and buffer 8 bytes to I2C buffer for matrix/7segment display if (functionLength == -1) { // first byte == message length functionLength = receivedByte; Serial.print("L"); Serial.println(functionLength); } else { I2CBuffer[functionLength - 1] = receivedByte; // Serial.print("I2C Buffer "); // Serial.print(functionLength); // Serial.println(I2CBuffer[functionLength - 1]); functionLength --; if (functionLength == 0) { writeI2C(); // Serial.print("I2C DONE"); } } break; case 5: readPin = int(receivedByte); Serial.print("AnalogRead pin "); Serial.print(readPin); pinMode(readPin, INPUT); Serial.print(" = "); Serial.println(analogRead(readPin)); returnByte = map(analogRead(readPin), 0, 1023, 0, 255); // now put that value into the buffer at 0 and signal ready to send *** inputBuffer[0] = returnByte; bufferLength[0] = 1; IOMODERESET(TIMESINCELASTBYTE, "readpin"); // SEEMS TO GET MISSED? IO_MODE = -1; functionLength = 0; break; case 7: // send inputbuffer contents to apple // *** send only N bytes, zero padded /* get length of expected message = L move L bytes from inputbuffer to outputbuffer set outputpointer = 0, wait for latch */ // first byte = expected message length functionLength = receivedByte; Serial.print("SENDING "); // Serial.print(functionLength); Serial.println(functionLength); // Serial.println("SENT"); outputPointer = 0; // buffer sent, start fresh. functionLength = 0; inputPointer = 0; // buffer sent, start fresh. bufferLength[0] = 0; // buffer sent, start fresh. break; case 8: // DEBUG MODE functionLength = -1; //Serial.print(receivedByte); Serial.print(char(receivedByte)); break; default: Serial.print("NO MODE FOR BYTE. "); Serial.println(receivedByte); break; } } } void setRGB(byte inByte, int bytePosition) { switch (bytePosition) { case 3: analogWrite(RGB_RED, 255 - inByte); break; case 2: analogWrite(RGB_GREEN, 255 - inByte); break; case 1: analogWrite(RGB_BLUE, 255 - inByte); break; default: analogWrite(RGB_RED, 255 - inByte); analogWrite(RGB_GREEN, 255 - inByte); analogWrite(RGB_BLUE, 255 - inByte); } // Serial.println(inByte); } void setRGBOneShot(int REDInt, int GREENInt, int BLUEInt) { analogWrite(RGB_RED, 255 - REDInt); analogWrite(RGB_GREEN, 255 - GREENInt); analogWrite(RGB_BLUE, 255 - BLUEInt); } byte arrayToByte(int arr[], int len) { // Convert -1 to 0 and pack the array into a byte int i; byte result = 0; for (i = 0; i < len; i++) { if (arr[i] == 0) { result &= ~(0 << i); } else { result |= (1 << i); } } return result; } void drawPixel(int16_t x, int16_t y, uint16_t color) { // if ((y < 0) || (y >= 8)) return; // if ((x < 0) || (x >= 8)) return; // wrap around the x x += 7; // 0,0 => 7,0 // 7,0 => 14,0 // 1,0 => 8,0se x %= 8; // 7,0 => 7,0 // 14,0 => 6,0 // 8,0 => 0,0 if (color) { I2CBuffer[y] |= 1 << x; // 0,0,1 => 10000000 // 1,0,1 => 00000001 } else { I2CBuffer[y] &= ~(1 << x); } Serial.print(x); Serial.println(y); Serial.println(I2CBuffer[y], BIN); } void writeI2C(void) { // writes bytes out to HT16K33 controller via I2C. //Needs a general purpose I2C version that will work with more devices i2c_start(i2c_addr); i2c_write(0x00); // start address $00 for (uint8_t i = 0; i < 8; i++) { // Serial.print("I2C write "); // Serial.println(I2CBuffer[i]); // rotate the byte right by 1. "carry" bit gets rolled around I2CBuffer[i] = ((I2CBuffer[i] & 0x01) ? 0x80 : 0x00) | (I2CBuffer[i] >> 1); // seriously? ROR. i2c_write(I2CBuffer[i]); // write the bits to turn on/off pixels i2c_write(0xff); // control/bitmask? } i2c_stop(); // Serial.println("I2C WROTE"); } void ClearI2C(void) { for (uint8_t i = 0; i < 8; i++) { I2CBuffer[i] = 0; } writeI2C(); }