News:

The forums are under a spambot siege. Registration has been disabled temporarily. Please contact me to register an account.

Main Menu
Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - CupOfTea

#1
// 30/6/18 Tachometer code modified 4 output to a serial LCD with rpm and bar graph
/*
Author: Jeffrey Nelson <nelsonjm@macpod.net>
Description: Very quickly written code to read tachometer port data of the SX2 mini mill and likely the SC2 mini lathe.
   for more information check http://macpod.net
  This code was written on an 5v 16Mhz Arduino. I'm planning on writing

Tachometer Port Interface:
  2 GND
  2 5V+
  1 LCDCS - Frame indicator,  pulled low during the transmission of a frame.
  1 LCDCL - Clock line, pulled low when data should be read (we read on the falling edges)
  1 LCDDI - Data line.

Data information:
  Reports if the spindel is stopped or running
  Reports speed of the spindel in 10rpm increments

Data format:
  Every .75 seconds a packet is sent over the port.
  Each packet consists of:
        (ONLY If the mill uses the new mill protocol): a 36 bit header
        Following this potential header are 4 frames. Each Frame consists of 17 bytes.
        The first 8 bits represent an address, and the other bits represent data.

  Frame 0: Represents 7-segment data used for 1000's place of rpm readout.
    Address: 0xA0
    Data: First bit is always 0
      Next 7 bits indicate which of the 7-segments to light up.
      Last bit is always 0
  Frame 1: Represents 7-segment data used for 100's place of rpm readout.
    Address: 0xA1
    Data: First bit is always 0
      Next 7 bits indicate which of the 7-segments to light up.
      Last bit is always 0
  Frame 2: Represents 7-segment data used for 10's place of rpm readout.
    Address: 0xA2
    Data: First bit is always 0
      Next 7 bits indicate which of the 7-segments to light up.
      Last bit is 1 if the spindel is not rotating, 0 otherwise.
  Frame 3: Represents 7-segment data used for 1's place of rpm readout. This isn't used.
    Address: 0xA3
    Data: This is always 0x20




7-segment display layout:
  d 
c g
  f 
b e
  a

abcdefg
00 = 1111101
01 = 0000101
02 = 1101011
03 = 1001111
04 = 0010111
05 = 1011110
06 = 1111110
07 = 0001101
08 = 1111111
09 = 1011111

*/

#include <avr/io.h>
#include <avr/interrupt.h>

#define LCDCS 2

#define LCDCL 3
#define LCDCL_INTERRUPT 1

#define LCDDI 4

#define PACKET_BITS 68
// For newer mills there is a 36 bit header.
#define PACKET_BITS_HEADER 36

// Uncomment for newer mill protocol
#define PACKET_BITS_COUNT PACKET_BITS+PACKET_BITS_HEADER
// Uncomment for old mill protocol
//#define PACKET_BITS_COUNT PACKET_BITS

#define MAXCOUNT 503500

volatile uint8_t packet_bits[PACKET_BITS_COUNT];
volatile uint8_t packet_bits_pos;

void setup() {
  Serial.begin(9600);
  pinMode(LCDCS, INPUT);
  pinMode(LCDCL, INPUT);
  pinMode(LCDDI, INPUT);

// Serial.write(0xFE);  Serial.write(0x0C);            // display on and hide cursor
//  Serial.write(0xFE);  Serial.write(0x01);            // clear screen
//  Serial.println("   Lathe RPM     Boo is stupid  ");
//  Serial.write(0x7C);  Serial.write(0x0A);            // set 0a set splash 09 toggleon/off
//  Serial.write(0x7C);  Serial.write(0x9D);            // backlight full
//  Serial.write(0x7C);  Serial.write(0x04);            // 16
//  Serial.write(0x7C);  Serial.write(0x06);            // 2

  writecharLCD();                  // at bot sets up custom chars

  EIMSK |= (1<<INT1);  //Enable INT1

    //Trigger on falling edge of INT1//
  EICRA |= (1<<ISC11);
  EICRA &= ~(1<<ISC10);

  TIMSK0 &= ~(1<<TOIE0); // Disable timer0
}

void loop() {
  int i;
  int rpm;
  unsigned long count;
  packet_bits_pos = 0;

  while (digitalRead(LCDCS) == LOW); // Wait to end of packet if we are in one.
  block_delay(227272); // ~200ms delay


  // Now we are ready to read a packet..
  EIMSK |= (1 << INT1);

  for (count = 0; packet_bits_pos < PACKET_BITS_COUNT && count < MAXCOUNT; count++) { // Loop until all bits are read or we timeout ~600ms
    asm("nop");
  }

  EIMSK &= ~(1 << INT1);

  if (packet_bits_pos == PACKET_BITS_COUNT && count < MAXCOUNT) {
    /*
     // Debug stuff...
     print_bits(0, 8); // Register 0
     Serial.print(": ");
     print_bits(8, 9);
     Serial.print("\t");
     
     print_bits(17, 8); // Register 1
     Serial.print(": ");
     print_bits(25, 9);
     Serial.print("\t");   
     
     print_bits(34, 8); // Register 2
     Serial.print(": ");   
     print_bits(42, 9);
     Serial.print("\t");   
     
     print_bits(51, 8); // Register 3
     Serial.print(": ");   
     print_bits(59, 9);
     Serial.print("\n");
     */

 
 
    rpm = get_rpm();
    if (rpm == -1) {
        Serial.println("");
        Serial.write(0xFE); Serial.write(0x01);    // clear display & setCursor 0,0
        Serial.print("Data just Silly");
      }
      else if (rpm == 0) {
         Serial.println("");
        Serial.write(0xFE); Serial.write(0x01);    // clear display & setCursor 0,0
//        Serial.print("Stopped");
        Serial.write(6);
        Serial.print(" Awaiting your     Pleasure ");
        Serial.write(7);
      }
      else {
        Serial.println("");
        Serial.write(0xFE); Serial.write(0x01);    // clear display & setCursor 0,0
        if ( rpm < 10 ) Serial.print(" ");
        if ( rpm < 100 ) Serial.print(" ");
        if ( rpm < 1000 ) Serial.print(" ");
        Serial.print(rpm, DEC);
        Serial.print(" rpm");
        LCDbar(rpm/25);                  // prercent of max
      }
    }
  else {
//    Serial.print(".");
//    Serial.write(0xFE); Serial.write(0x01);    // clear display & setCursor 0,0
//    Serial.print("Sorry no data.");
  }

  for (packet_bits_pos = 0; packet_bits_pos < PACKET_BITS_COUNT; packet_bits_pos++) {
    packet_bits[packet_bits_pos] = 0;
  }

}


//----------------------------------------------------------------------------------------------------
// Assumes 68 bits were received properly. Returns the spindle speed or -1 if the values are absurd.
int get_rpm()
{
  int temp, ret = 0;
  if (build_address(0) != 0xA0) {
    return -1;
  }
  temp = get_digit_from_data(build_data(8));
  if (temp == -1) {
    return -1;
  }
  ret += temp*1000;

  if (build_address(17) != 0xA1) {
    return -1;
  }
  temp = get_digit_from_data(build_data(25));
  if (temp == -1) {
    return -1;
  }
  ret += temp*100; 
 
  if (build_address(34) != 0xA2) {
    return -1;
  }
  temp = get_digit_from_data(build_data(42));
  if (temp == -1) {
    return -1;
  }
  ret += temp*10;


  if (build_address(51) != 0xA3) {
    return -1;
  }
  temp = build_data(59);
  if (temp != 0x20) {
    return -1;
  }
 
  return ret;
}

// An address is 8 bits long
uint8_t build_address(uint8_t start_address)
{
  uint8_t ret = 0x1;
  uint8_t i;

  if (PACKET_BITS_COUNT != PACKET_BITS) {
    // Compensate for header
    start_address += PACKET_BITS_HEADER;
  }

  for (i = start_address; i < start_address + 8; i++) {
    ret = (ret << 1) ^ ((packet_bits[i] & B00010000) ? 1 : 0);
  }
  return ret;
}

// Data is 9 bits long
uint16_t build_data(uint8_t start_address)
{
  uint16_t ret = 0;
  uint8_t i;

  if (PACKET_BITS_COUNT != PACKET_BITS) {
    // Compensate for header
    start_address += PACKET_BITS_HEADER;
  }

  for (i = start_address; i < start_address + 9; i++) {
    ret = (ret << 1) ^ ((packet_bits[i] & B00010000) ? 1 : 0);
  }
  return ret;
}

int get_digit_from_data(uint16_t data)
{
  uint16_t segments = (data & 0xFE) >> 1;
  int ret = 0;
  switch(segments) {
  case 0x7D:
    ret = 0;
    break;
  case 0x05:
    ret = 1;
    break;
  case 0x6B:
    ret = 2;
    break;
  case 0x4F:
    ret = 3;
    break;
  case 0x17:
    ret = 4;
    break;
  case 0x5E:
    ret = 5;
    break;
  case 0x7E:
    ret = 6;
    break;
  case 0x0D:
    ret = 7;
    break;
  case 0x7F:
    ret = 8;
    break;
  case 0x5F:
    ret = 9;
    break;
  default:
    ret = -1;
    break;
  }
  return ret;
}

// Returns 1 if stopped, 0 otherwise.
uint8_t spindle_stopped(uint16_t data)
{
  return data & 0x1;
}


//-----------------------------------------------------------------------------------------------------



void print_bits(int start, int len) {
  if (PACKET_BITS_COUNT != PACKET_BITS) {
    // Compensate for header
    start += PACKET_BITS_HEADER;
  }

  for (int i = start; i < start+len; i++) {
    if (packet_bits[i] & B00010000) {
      Serial.print('1');
    }
    else {
      Serial.print('0');
    }
  }
}



// 100000 ~= 88ms
void block_delay(unsigned long units)
{
  unsigned long i;

  for (i = 0; i < units; i++) {
    asm("nop"); // Stop optimizations
  }
}




SIGNAL(INT1_vect)
{
  packet_bits[packet_bits_pos] = PIND;
  packet_bits_pos++;
}


void writecharLCD() {
uint8_t charArray[][8] = {                         //Self defined bar characters
   0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,         // bottom line     
   0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x1F,         // 1
   0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,         // 11
   0x00,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1F,         // 111
   0x00,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1F,         // 1111
   0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,         // 11111
   0x00,0x0A,0x00,0x11,0x1E,0x00,0x00,0x00,         // happy
   0x00,0x0A,0x15,0x11,0x1A,0x04,0x00,0x00          // heart
   };

for (int j = 0; j < 8; j++) {
   Serial.write(254);
   Serial.write(64+j*8);

   for (int i = 0; i < 8; i++) {
     Serial.write(charArray[j][i]);
   } //end inner char for
}// end outter for 
delay(100); 
}

void LCDbar(int percent) {
Serial.write(0xFE);
Serial.write(192);              // line 1 (not 0)
if (percent > 100) {
   percent = 100;
}

int dots = 80*percent/100;
int chars = dots/5;
if (chars > 0) {
   for (int i = 1; i <= chars; i++) {
     Serial.write(5);
   }
}
Serial.write(dots%5);
while (chars < 15) {                  // clear to end
   Serial.write(0);
   chars ++;
}
}


void lcdPos(int x, int y){  //takes x (0-15) and y (0-1) position arguments
if (y==0)  y=128;
if (y==1)  y=192;
Serial.write(0xFE);
Serial.write(x+y);
}