IC Tester

Although Small Scale Integrated Circuits have become outdated nowadays many users have been posing a demand for checking logic integrated circuits with their Arduinos, and in fact there are some projects published on the net.

When I happened to come across this: Arduino-IC-Tester I was impressed by the way JorBi implemented a language to describe the logic functions.

(There already exists a language to describe logic hardware components called VERILOG but it will not be implemented on the Arduino platform.) But when I saw he (or she?) is using an AT24C512 EEPROM to store the data I lost interest in this project as I had none in my spare parts.

At a second glimpse, I realized that all the data would fit into the Flash of an Arduino when you store it as const PROGMEM.

You even save some space as the routine for transferring data to the EEPROM via I2C will be omitted. You save more space when you delete data which are completely useless (duplicate functions as the pin-compatible chips 7413, 7418, 7422, 7420, 7440). And when you use the Serial Monitor instead of the LCD you even free the two pins A4 and A5.

That was the moment when I dug into this project. I realized that it could be speeded up to less than 200 milliseconds (!) for a complete test. Less than the time to release a button.
So entering the part number does not make sense any longer. I modified everything in the way when you press the RESET button a complete test will be performed and the result(s) if any will be displayed on the Serial monitor.
Oh, by the way: there is no law telling you there must be any code in the loop-function. Once setup is finished all is done.
There is no need for more keys. You do not need the A6/A7 pins which are not available on standard Arduinos.

I also shifted the pins configuration to get the shortest connections between the Arduino Nano and the IC socket. And I still have the pins 11, 12, and 13 in case I want to add an SPI TFT display.

With all this modifications, the code shrinked from 650 lines down to only 125 lines.
The code size is only 23600 bytes, plenty of room for more extras and additional IC types.

Due to the limitations of the ATmega328, JorBi decided to use 680 Ω resistors. That is why not all TTL families can be detected.

I added a debug-option in case you want to see the results of all the tests. Just modify the #define DEBUG accordingly.

As I had no 16-pin Textool I used a longer one and covered the unused pins.

And that is my source:

#include "IC_data1.h"
#define DE-BUG
const long MAGIC = 0x40007400;
long powerOn __attribute__ ((section(".noinit")));
char Signal[16];
byte PinOut[16];
byte Pin_max;      // Maximum number of Pins of the IC to be tested
word j = 0;        // pointer to IC-data in Flash

void setup() {
  if (powerOn != MAGIC) {
    powerOn = MAGIC;
    // print only on PowerOn:
    char s[] = __FILE__;
    byte c = sizeof(s);
    while ( (c > 0) && (s[c] != '\\')) c--;
    char *u = &s[++c];
    Serial.print(F(" compile time: "));
    Serial.print(F(", "));
  const byte Pin14[14] = {     A1, A2, A3, A4, A5, 10, A7, 2, 3, 4, 5, 6, 7, 8 };
  const byte Pin16[16] = { A0, A1, A2, A3, A4, A5, 10, A7, 2, 3, 4, 5, 6, 7, 8, 9 };
  // A7: fake for GND. Unused pins: A6, 0, 1, 11, 12, 13. SPI pins still available
  Serial.print(F("Testing ...\nmatches:\n"));
  long type = getNextName();
  while (type > 0) {
    Pin_max = getPinNum();
    if (Pin_max == 14) for (byte i = 0; i < 14; i++) PinOut[i] = Pin14[i];
    if (Pin_max == 16) for (byte i = 0; i < 16; i++) PinOut[i] = Pin16[i];
    boolean thisPartOk = true;    // to start with
    while (thisPartOk) {
      word jj = j;
      for (byte i = 0; i < Pin_max; i++) Signal[i] = getChar();
      if (Signal[0] == '<') {
        j = jj;    // rewind, we just read the next type number
        break;     // and do not test with this pattern
      thisPartOk = thisPartOk && test1(type);  // call the test procedure
    if (thisPartOk) Serial.println(type); 
    type = getNextName();         // read next type number
  Serial.println(F("Ready.\nPress RESET for a new test\n"));

void loop() {} // nothing more to be done

void set(byte pin, byte mode, boolean v) {
  digitalWrite(pin, v);
  pinMode(pin, mode);

boolean get(byte pin) {
  return digitalRead(pin);

boolean test1(long type) {
  // prepare:
  for (byte i = 0; i < Pin_max; i++)
    switch (Signal[i]) {
      case 'V': set(PinOut[i], OUTPUT, HIGH); break; // +Vcc
      case 'L':
      case 'H': set(PinOut[i], INPUT_PULLUP, LOW); break;
  // set signals:
  for (byte i = 0; i < Pin_max; i++)
    switch (Signal[i]) {
      case '0':
      case 'C': set(PinOut[i], OUTPUT, LOW); break;
      case '1': set(PinOut[i], OUTPUT, HIGH); break;
  // trigger clock:
  for (byte i = 0; i < Pin_max; i++) if (Signal[i] == 'C') pinMode(PinOut[i], INPUT_PULLUP);
  // set it back LOW:
  for (byte i = 0; i < Pin_max; i++) if (Signal[i] == 'C') set(PinOut[i], OUTPUT, LOW);
  // read and compare the outputs:
  boolean result = true;
  for (byte i = 0; i < Pin_max; i++) {
    // Should be HIGH but is LOW
    if ( (Signal[i] == 'H') && !get(PinOut[i]) ) result = false; else
      // Should be LOW but is HIGH
      if ( (Signal[i] == 'L') && get(PinOut[i]) ) result = false;
#ifdef DEBUG
#include "debug.h"
  return result;

char getChar() {
  return pgm_read_byte_near(ic_data + j++);

long getNextName() {
  char c;
  boolean term = false;
  while (!term) {
    c = getChar();
    if (c == '<') term = true;
  char name[6];
  byte i = 0;
  while (term) {
    c = getChar();
    if (c == '>') term = false;
    else name[i++] = c;
  name[i] = 0;
  return atol(name);

byte getPinNum() {
  char buf[3];
  buf[0] = getChar();
  buf[1] = getChar();
  buf[2] = 0;
  getChar(); // overread the space
  return atoi(buf);

And here you will find

contact: nji(at)gmx.de