/******************************************************** JI3BNB SSTV ENCODER (ROBOT B/W 8S MODE) v1.4 2016.04.08 ARDUINO MEGA 2560 + ETHERNET SHIELD R3 + AD9850 DDS ETHERNET SHIELD ACTS JUST AS A "SD CARD SHIELD" --- THIS CODE IS IN THE PUBLIC DOMAIN --- /********************************************************/ #include <SPI.h> //IDE 1.5.0 or later #include <SD.h> const int chipSelect = 4; File file; #include <TimerOne.h> #define CLK 6 // MEGA:PORT_H3 #define FQ 7 // MEGA:PORT_H4 #define DATA 8 // MEGA:PORT_H5 #define RST 9 // MEGA:PORT_H6 volatile long oFrq = 7171000; // ***** set operating Frequency here in Hz ***** volatile byte mode; volatile byte sSq; volatile int line; volatile int ti; const boolean vox = 1; // ***** USE VOX TONE OR NOT **** const boolean aRf = 0; // ***** DDS VFO OUTPUT MODE ***** //AF: 0 //RF: 1 char line00[7] = "CQSSTV"; // ***** TEXT MESSAGE 6x2 ***** char line01[7] = "JI3BNB"; const uint8_t font_a[] = {0x00, 0x18, 0x24, 0x62, 0x62, 0x62, 0x7E, 0x62, 0x62, 0x62, 0x00}; const uint8_t font_b[] = {0x00, 0x7C, 0x32, 0x32, 0x32, 0x3C, 0x32, 0x32, 0x32, 0x7C, 0x00}; const uint8_t font_c[] = {0x00, 0x3C, 0x62, 0x62, 0x60, 0x60, 0x60, 0x62, 0x62, 0x3C, 0x00}; const uint8_t font_d[] = {0x00, 0x7C, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x7C, 0x00}; const uint8_t font_e[] = {0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x7E, 0x00}; const uint8_t font_f[] = {0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x00}; const uint8_t font_g[] = {0x00, 0x3C, 0x62, 0x62, 0x60, 0x60, 0x66, 0x62, 0x62, 0x3C, 0x00}; const uint8_t font_h[] = {0x00, 0x62, 0x62, 0x62, 0x62, 0x7E, 0x62, 0x62, 0x62, 0x62, 0x00}; const uint8_t font_i[] = {0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00}; const uint8_t font_j[] = {0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x4C, 0x4C, 0x4C, 0x38, 0x00}; const uint8_t font_k[] = {0x00, 0x62, 0x64, 0x68, 0x70, 0x68, 0x64, 0x62, 0x62, 0x62, 0x00}; const uint8_t font_l[] = {0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00}; const uint8_t font_m[] = {0x00, 0x42, 0x62, 0x76, 0x6A, 0x62, 0x62, 0x62, 0x62, 0x62, 0x00}; const uint8_t font_n[] = {0x00, 0x42, 0x62, 0x72, 0x6A, 0x66, 0x62, 0x62, 0x62, 0x62, 0x00}; const uint8_t font_o[] = {0x00, 0x3C, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x3C, 0x00}; const uint8_t font_p[] = {0x00, 0x7C, 0x62, 0x62, 0x62, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x00}; const uint8_t font_q[] = {0x00, 0x3C, 0x62, 0x62, 0x62, 0x62, 0x62, 0x6A, 0x6A, 0x3C, 0x08}; const uint8_t font_r[] = {0x00, 0x7C, 0x62, 0x62, 0x62, 0x7C, 0x68, 0x64, 0x62, 0x62, 0x00}; const uint8_t font_s[] = {0x00, 0x3C, 0x62, 0x60, 0x60, 0x3C, 0x06, 0x06, 0x46, 0x3C, 0x00}; const uint8_t font_t[] = {0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}; const uint8_t font_u[] = {0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x3C, 0x00}; const uint8_t font_v[] = {0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x22, 0x14, 0x08, 0x00}; const uint8_t font_w[] = {0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x6A, 0x76, 0x62, 0x42, 0x00}; const uint8_t font_x[] = {0x00, 0x42, 0x62, 0x74, 0x38, 0x1C, 0x2E, 0x46, 0x42, 0x42, 0x00}; const uint8_t font_y[] = {0x00, 0x42, 0x62, 0x74, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}; const uint8_t font_z[] = {0x00, 0x7E, 0x06, 0x0E, 0x0C, 0x18, 0x30, 0x70, 0x60, 0x7E, 0x00}; const uint8_t font_0[] = {0x00, 0x3C, 0x62, 0x62, 0x66, 0x6A, 0x72, 0x62, 0x62, 0x3C, 0x00}; const uint8_t font_1[] = {0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}; const uint8_t font_2[] = {0x00, 0x3C, 0x46, 0x06, 0x06, 0x1C, 0x20, 0x60, 0x60, 0x7E, 0x00}; const uint8_t font_3[] = {0x00, 0x3C, 0x46, 0x06, 0x06, 0x1C, 0x06, 0x06, 0x46, 0x3C, 0x00}; const uint8_t font_4[] = {0x00, 0x0C, 0x1C, 0x2C, 0x4C, 0x4C, 0x7E, 0x0C, 0x0C, 0x0C, 0x00}; const uint8_t font_5[] = {0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x46, 0x3C, 0x00}; const uint8_t font_6[] = {0x00, 0x3C, 0x62, 0x60, 0x60, 0x7C, 0x62, 0x62, 0x62, 0x3C, 0x00}; const uint8_t font_7[] = {0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00}; const uint8_t font_8[] = {0x00, 0x3C, 0x62, 0x62, 0x62, 0x3C, 0x62, 0x62, 0x62, 0x3C, 0x00}; const uint8_t font_9[] = {0x00, 0x3C, 0x46, 0x46, 0x46, 0x3E, 0x06, 0x06, 0x46, 0x3C, 0x00}; const uint8_t font_slu[] = {0x00, 0x00, 0x02, 0x06, 0x0E, 0x1C, 0x38, 0x70, 0x60, 0x40, 0x00}; const uint8_t font_das[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00}; const uint8_t font_dot[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00}; const uint8_t font_que[] = {0x00, 0x3C, 0x46, 0x06, 0x06, 0x0C, 0x10, 0x00, 0x30, 0x30, 0x00}; const uint8_t font_eks[] = {0x00, 0x18, 0x18, 0x18, 0x18, 0x10, 0x10, 0x00, 0x18, 0x18, 0x00}; const uint8_t font_col[] = {0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00}; const uint8_t font_spa[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t pictArea[4800]; //80*60 void shortPulse (char PIN) { digitalWrite(PIN, 1); digitalWrite(PIN, 0); } void setFreq(double freq) { //--calculate int32_t d_Phase = freq * pow(2, 32) / 125000000; //--send first 32bit for (int i=0; i<32; i++, d_Phase>>=1) { if(d_Phase & 1 == 1) { PORTH |= _BV(5); //--data } else { PORTH &= ~_BV(5); //--data } //--short pulse clk PORTH |= _BV(3); PORTH &= ~_BV(3); } //--send rest 8bit PORTH &= ~_BV(5); //--data for (int i=0; i<8; i++) { //--short pulse clk PORTH |= _BV(3); PORTH &= ~_BV(3); } //--finish, short pulse fq PORTH |= _BV(4); delayMicroseconds(30); PORTH &= ~_BV(4); } void setup() { //--SD CARD Serial.begin(9600) ; pinMode(53, OUTPUT) ; pinMode(31, OUTPUT) ; //ERROR LAMP if (!SD.begin(chipSelect)) { Serial.println("ERROR: INSERT SD CARD AND RESTART") ; digitalWrite(31, HIGH); return ; } Serial.println("OK: SD CARD INITIALIZED") ; //-- Timer1.initialize(); Timer1.attachInterrupt(timer1_interrupt, 1000); digitalWrite(47, 1); //INTERNAL PULL UP digitalWrite(49, 1); //INTERNAL PULL UP //--DDS pinMode(CLK, OUTPUT); pinMode(FQ, OUTPUT); pinMode(DATA, OUTPUT); pinMode(RST, OUTPUT); //--reset shortPulse(RST); shortPulse(CLK); //--change mode shortPulse(FQ); //--done delay(5); //-- } void timer1_interrupt(void) { int p; if(sSq == 3) { if(mode == 1) { if(ti < 160) { p = 80 * (line / 2) + (ti / 2); if(aRf == 0) { if(pictArea[p] <= 0x0F){setFreq(1500);} else if(pictArea[p] <= 0x1F){setFreq(1553);} else if(pictArea[p] <= 0x2F){setFreq(1607);} else if(pictArea[p] <= 0x3F){setFreq(1660);} else if(pictArea[p] <= 0x4F){setFreq(1713);} else if(pictArea[p] <= 0x5F){setFreq(1767);} else if(pictArea[p] <= 0x6F){setFreq(1820);} else if(pictArea[p] <= 0x7F){setFreq(1873);} else if(pictArea[p] <= 0x8F){setFreq(1927);} else if(pictArea[p] <= 0x9F){setFreq(1980);} else if(pictArea[p] <= 0xAF){setFreq(2033);} else if(pictArea[p] <= 0xBF){setFreq(2087);} else if(pictArea[p] <= 0xCF){setFreq(2140);} else if(pictArea[p] <= 0xDF){setFreq(2193);} else if(pictArea[p] <= 0xEF){setFreq(2247);} else {setFreq(2300);} } else { if(pictArea[p] <= 0x0F){setFreq(oFrq - 1500);} else if(pictArea[p] <= 0x1F){setFreq(oFrq - 1553);} else if(pictArea[p] <= 0x2F){setFreq(oFrq - 1607);} else if(pictArea[p] <= 0x3F){setFreq(oFrq - 1660);} else if(pictArea[p] <= 0x4F){setFreq(oFrq - 1713);} else if(pictArea[p] <= 0x5F){setFreq(oFrq - 1767);} else if(pictArea[p] <= 0x6F){setFreq(oFrq - 1820);} else if(pictArea[p] <= 0x7F){setFreq(oFrq - 1873);} else if(pictArea[p] <= 0x8F){setFreq(oFrq - 1927);} else if(pictArea[p] <= 0x9F){setFreq(oFrq - 1980);} else if(pictArea[p] <= 0xAF){setFreq(oFrq - 2033);} else if(pictArea[p] <= 0xBF){setFreq(oFrq - 2087);} else if(pictArea[p] <= 0xCF){setFreq(oFrq - 2140);} else if(pictArea[p] <= 0xDF){setFreq(oFrq - 2193);} else if(pictArea[p] <= 0xEF){setFreq(oFrq - 2247);} else {setFreq(oFrq - 2300);} } } else if(ti == 160) { line++; sSq = 2; } if(line == 120) { sSq = 0; } ti++; } else if(mode == 0) { if(line >= 40 && line <= 61) { if(ti < 56) { p = 56 * (line - 40) + ti; if(aRf == 0) { if(pictArea[p]) { setFreq(2300); } else { setFreq(1500); } } else { if(pictArea[p]) { setFreq(oFrq - 2300); } else { setFreq(oFrq - 1500); } } } else if(ti == 56) { line++; sSq = 2; } } else { if(aRf == 0) { switch(ti) { case 0: setFreq(1500); break; case 14: setFreq(1767); break; case 28: setFreq(2033); break; case 42: setFreq(2300); break; case 56: line++; if(line == 120) { sSq = 0; } else { sSq =2; } break; } } else { switch(ti) { case 0: setFreq(oFrq - 1500); break; case 14: setFreq(oFrq - 1767); break; case 28: setFreq(oFrq - 2033); break; case 42: setFreq(oFrq - 2300); break; case 56: line++; if(line == 120) { sSq = 0; } else { sSq =2; } break; } } } ti++; } } } void loop() { int x; int y; int i; int p; if(sSq == 0) { setFreq(2); while(sSq == 0) { if(digitalRead(47) == 0) // - SEND SW - { sSq = 1; } else { delay(30); } } } if(sSq == 1) { boolean err; if(digitalRead(49) == 1) // - MODE SW - { err = 0; mode = 1; //PICTURE MODE Timer1.setPeriod(353); // ***** SLANT ADJUST ***** file = SD.open("sstv.bmp",FILE_READ) ; if (file) { uint16_t addr; uint8_t bData; uint8_t offset; uint8_t cDepth; byte rgbCnt; float pxR; float pxG; float pxB; float pxGr; uint8_t pxGr_; for(i = 0; i < 4800; i++) { pictArea[i] = 0xFF; } x = 0; y = 0; addr = 0; while(file.available()) { bData = file.read(); //Serial.print(addr, HEX); //Serial.print(":"); //Serial.println(bData); //delay(300); if(addr == 0x00) { if(bData != 0x42) //ASCII"B" { Serial.println("ERROR: THIS IS NOT BMP FILE"); err = 1; goto fileCls; } } else if(addr == 0x01) { if(bData != 0x4D) //ASCII"M" { Serial.println("ERROR: THIS IS NOT BMP FILE"); err = 1; goto fileCls; } } else if(addr == 0x0A) { offset = bData; //STARTING ADDRESS OF PICTURE DATA } else if(addr == 0x0E) { if(bData != 40) //--WINDOWS BITMAP(40)-- --OS/2 BITMAP(12)-- { Serial.println("ERROR: THIS IS NOT -WINDOWS- BITMAP FILE"); err = 1; goto fileCls; } } else if(addr == 0x12) { if(bData != 80) //PICTURE WIDTH { Serial.println("ERROR: PICTURE WIDTH MUST BE 80 PX"); err = 1; goto fileCls; } } else if(addr == 0x13) { if(bData != 0) //HIGHER BIT { Serial.println("ERROR: PICTURE WIDTH MUST BE 80 PX"); err = 1; goto fileCls; } } else if(addr == 0x16) { if(bData != 60) //PICTURE HEIGHT { Serial.println("ERROR: PICTURE HEIGHT MUST BE 60 PX"); err = 1; goto fileCls; } } else if(addr == 0x17) //HIGHER BIT { if(bData != 0) { Serial.println("ERROR: PICTURE HEIGHT MUST BE 60 PX"); err = 1; goto fileCls; } } else if(addr == 0x1C) { cDepth = bData; if(cDepth != 24 && cDepth != 32) //COLOR DEPTH 1,4,8,24,32 BIT { Serial.println("ERROR: COLOR MUST BE 24 OR 32 BIT"); err = 1; goto fileCls; } } else if(addr == 0x1E) { if(bData != 0) //COMPRESSION TYPE 0,1,2,3 { Serial.println("ERROR: MUST BE -NO- COMPRESSION FILE"); err = 1; goto fileCls; } } else if(addr >= 0x0E && addr >= offset) { switch(rgbCnt) { case 0: pxB = bData; //-- BLUE -- rgbCnt++; break; case 1: pxG = bData; //-- GREEN -- rgbCnt++; break; case 2: pxR = bData; //-- RED -- pxGr = ((pxB * 0.11) + (pxG * 0.59) + (pxR * 0.30)); //-- Convert to Grey Scale -- pxGr_ = (uint8_t)pxGr; p = ((59 - y) * 80) + x; pictArea[p] = pxGr_; x++; if(x == 80) { y++; x = 0; } if(y == 60) { goto fileCls; } if(cDepth == 32) { rgbCnt++; } else { rgbCnt = 0; } break; case 3: rgbCnt = 0; //-- RESERVE -- break; } } addr++; } fileCls: file.close(); } else { Serial.println("ERROR: CAN'T OPEN FILE") ; err = 1; } if(err == 1) { digitalWrite(31, HIGH); //ERROR LAMP while(true); //STOP } } else { mode = 0; //TEXT MODE Timer1.setPeriod(1001); // ***** SLANT ADJUST ***** for(p = 0; p < 1232; p++) { pictArea[p] = 0xFF; } for(i = 0; i < 12; i++) { char ch; if(i < 6) { ch = line00[i]; } else { ch = line01[i - 6]; } for(y = 0; y < 11; y++) { for(x = 0; x < 8; x++) { if(i < 6) { p = 4 + (56 * y) + (8 * i) + x; } else { p = 4 + (56 * 11) + (56 * y) + (8 * (i - 6)) + x; } uint8_t mask; if(x == 0){mask = B10000000;} else if(x == 1){mask = B01000000;} else if(x == 2){mask = B00100000;} else if(x == 3){mask = B00010000;} else if(x == 4){mask = B00001000;} else if(x == 5){mask = B00000100;} else if(x == 6){mask = B00000010;} else if(x == 7){mask = B00000001;} if(ch == 'A') { if((font_a[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'B') { if((font_b[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'C') { if((font_c[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'D') { if((font_d[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'E') { if((font_e[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'F') { if((font_f[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'G') { if((font_g[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'H') { if((font_h[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'I') { if((font_i[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'J') { if((font_j[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'K') { if((font_k[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'L') { if((font_l[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'M') { if((font_m[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'N') { if((font_n[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'O') { if((font_o[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'P') { if((font_p[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'Q') { if((font_q[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'R') { if((font_r[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'S') { if((font_s[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'T') { if((font_t[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'U') { if((font_u[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'V') { if((font_v[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'W') { if((font_w[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'X') { if((font_x[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'Y') { if((font_y[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == 'Z') { if((font_z[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '0') { if((font_0[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '1') { if((font_1[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '2') { if((font_2[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '3') { if((font_3[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '4') { if((font_4[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '5') { if((font_5[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '6') { if((font_6[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '7') { if((font_7[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '8') { if((font_8[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '9') { if((font_9[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '/') { if((font_slu[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '-') { if((font_das[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '.') { if((font_dot[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '?') { if((font_que[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == '!') { if((font_eks[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == ':') { if((font_col[y] & mask) != 0) { pictArea[p] = 0x00; } } else if(ch == ' ') { if((font_spa[y] & mask) != 0) { pictArea[p] = 0x00; } } } } } } if(mode == 0) { delay(800); } if(aRf == 0) { //--VOX TONE if(vox == 1) { setFreq(1900); delay(100); setFreq(1500); delay(100); setFreq(1900); delay(100); setFreq(1500); delay(100); setFreq(2300); delay(100); setFreq(1500); delay(100); setFreq(2300); delay(100); setFreq(1500); delay(100); } //--VIS CODE //VIS CODE for ROBOT B/W 8S is B0000010 (DECIMAL 2) setFreq(1900); delay(300); setFreq(1200); //BREAK delay(10); setFreq(1900); delay(300); setFreq(1200); //START BIT delay(30); setFreq(1300); //BIT 0 (LSB FIRST) delay(30); setFreq(1100); //BIT 1 delay(30); setFreq(1300); //BIT 2, 3, 4, 5, 6 delay(150); setFreq(1100); //EVEN PARITY delay(30); setFreq(1200); //STOP BIT delay(30); } else { //--VIS CODE setFreq(oFrq - 1900); delay(300); setFreq(oFrq - 1200); //BREAK delay(10); setFreq(oFrq - 1900); delay(300); setFreq(oFrq - 1200); //START BIT delay(30); setFreq(oFrq - 1300); //BIT 0 (LSB FIRST) delay(30); setFreq(oFrq - 1100); //BIT 1 delay(30); setFreq(oFrq - 1300); //BIT 2, 3, 4, 5, 6 delay(150); setFreq(oFrq - 1100); //EVEN PARITY delay(30); setFreq(oFrq - 1200); //STOP BIT delay(30); } //--VIS DONE line = 0; sSq = 2; } if(sSq == 2) { //--sync if(aRf == 0) { setFreq(1200); delayMicroseconds(10000); } else { setFreq(oFrq - 1200); delayMicroseconds(10000); } //-- ti = 0; sSq = 3; } }