/************************************************* JI3BNB SSTV ENCODER Robot B/W8 Format, TEXT MESSAGE / BMP FILE 2018.08.01 v2.01 ARDUINO "DUE" + ETHERNET SHIELD R3 + AD9850 DDS ETHERNET SHIELD ACTS JUST AS A "SD CARD SHIELD" --- THIS CODE IS IN THE PUBLIC DOMAIN --- /*************************************************/ #include <SPI.h> #include <SD.h> #include <DueTimer.h> File myFile; #define chipSelect 4 #define CLK 6 #define FQ 7 #define DATA 8 #define RST 9 volatile long oFrq = 7171000; // ***** set RF OUTPUT FREQUENCY 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[10] = "CQCQ SSTV"; // ***** TEXT MESSAGE 9x3 ***** char line01[10] = "DE JI3BNB"; char line02[10] = "GL PM74SS"; //FONTS const uint8_t fonts[43][11] = { {0x00, 0x18, 0x24, 0x62, 0x62, 0x62, 0x7E, 0x62, 0x62, 0x62, 0x00}, //00: A {0x00, 0x7C, 0x32, 0x32, 0x32, 0x3C, 0x32, 0x32, 0x32, 0x7C, 0x00}, //01: B {0x00, 0x3C, 0x62, 0x62, 0x60, 0x60, 0x60, 0x62, 0x62, 0x3C, 0x00}, //02: C {0x00, 0x7C, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x7C, 0x00}, //03: D {0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x7E, 0x00}, //04: E {0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x00}, //05: F {0x00, 0x3C, 0x62, 0x62, 0x60, 0x60, 0x66, 0x62, 0x62, 0x3C, 0x00}, //06: G {0x00, 0x62, 0x62, 0x62, 0x62, 0x7E, 0x62, 0x62, 0x62, 0x62, 0x00}, //07: H {0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00}, //08: I {0x00, 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x4C, 0x4C, 0x4C, 0x38, 0x00}, //09: J {0x00, 0x62, 0x64, 0x68, 0x70, 0x68, 0x64, 0x62, 0x62, 0x62, 0x00}, //10: K {0x00, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00}, //11: L {0x00, 0x42, 0x62, 0x76, 0x6A, 0x62, 0x62, 0x62, 0x62, 0x62, 0x00}, //12: M {0x00, 0x42, 0x62, 0x72, 0x6A, 0x66, 0x62, 0x62, 0x62, 0x62, 0x00}, //13: N {0x00, 0x3C, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x3C, 0x00}, //14: O {0x00, 0x7C, 0x62, 0x62, 0x62, 0x7C, 0x60, 0x60, 0x60, 0x60, 0x00}, //15: P {0x00, 0x3C, 0x62, 0x62, 0x62, 0x62, 0x62, 0x6A, 0x6A, 0x3C, 0x08}, //16: Q {0x00, 0x7C, 0x62, 0x62, 0x62, 0x7C, 0x68, 0x64, 0x62, 0x62, 0x00}, //17: R {0x00, 0x3C, 0x62, 0x60, 0x60, 0x3C, 0x06, 0x06, 0x46, 0x3C, 0x00}, //18: S {0x00, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, //19: T {0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x3C, 0x00}, //20: U {0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x62, 0x22, 0x14, 0x08, 0x00}, //21: V {0x00, 0x62, 0x62, 0x62, 0x62, 0x62, 0x6A, 0x76, 0x62, 0x42, 0x00}, //22: W {0x00, 0x42, 0x62, 0x74, 0x38, 0x1C, 0x2E, 0x46, 0x42, 0x42, 0x00}, //23: X {0x00, 0x42, 0x62, 0x74, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, //24: Y {0x00, 0x7E, 0x06, 0x0E, 0x0C, 0x18, 0x30, 0x70, 0x60, 0x7E, 0x00}, //25: Z {0x00, 0x3C, 0x62, 0x62, 0x66, 0x6A, 0x72, 0x62, 0x62, 0x3C, 0x00}, //26: 0 {0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, //27: 1 {0x00, 0x3C, 0x46, 0x06, 0x06, 0x1C, 0x20, 0x60, 0x60, 0x7E, 0x00}, //28: 2 {0x00, 0x3C, 0x46, 0x06, 0x06, 0x1C, 0x06, 0x06, 0x46, 0x3C, 0x00}, //29: 3 {0x00, 0x0C, 0x1C, 0x2C, 0x4C, 0x4C, 0x7E, 0x0C, 0x0C, 0x0C, 0x00}, //30: 4 {0x00, 0x7E, 0x60, 0x60, 0x60, 0x7C, 0x06, 0x06, 0x46, 0x3C, 0x00}, //31: 5 {0x00, 0x3C, 0x62, 0x60, 0x60, 0x7C, 0x62, 0x62, 0x62, 0x3C, 0x00}, //32: 6 {0x00, 0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00}, //33: 7 {0x00, 0x3C, 0x62, 0x62, 0x62, 0x3C, 0x62, 0x62, 0x62, 0x3C, 0x00}, //34: 8 {0x00, 0x3C, 0x46, 0x46, 0x46, 0x3E, 0x06, 0x06, 0x46, 0x3C, 0x00}, //35: 9 {0x00, 0x00, 0x02, 0x06, 0x0E, 0x1C, 0x38, 0x70, 0x60, 0x40, 0x00}, //36: / {0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00}, //37: - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00}, //38: . {0x00, 0x3C, 0x46, 0x06, 0x06, 0x0C, 0x10, 0x00, 0x30, 0x30, 0x00}, //39: ? {0x00, 0x18, 0x18, 0x18, 0x18, 0x10, 0x10, 0x00, 0x18, 0x18, 0x00}, //40: ! {0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00}, //41: : {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} //42: space }; uint8_t frameBuf[19200]; //160*120 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) { digitalWrite(DATA, HIGH); } else { digitalWrite(DATA, LOW); } shortPulse(CLK); } //--send rest 8bit digitalWrite(DATA, LOW); for (int i=0; i<8; i++) { shortPulse(CLK); } //--finish shortPulse(FQ); } void setup() { //--SD CARD Serial.begin(9600) ; pinMode(10, OUTPUT) ; pinMode(31, OUTPUT) ; //ERROR LAMP digitalWrite(31, LOW); if (!SD.begin(chipSelect)) { Serial.println("ERROR: INSERT SD CARD AND RESTART") ; digitalWrite(31, HIGH); return ; } Serial.println("SD CARD INITIALIZED") ; pinMode(47, INPUT); pinMode(49, INPUT); 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); //--dds reset shortPulse(RST); shortPulse(CLK); //--change mode shortPulse(FQ); delay(5); Timer1.attachInterrupt(timer1_interrupt).start(352); // ***** 354(uS/px) +/- SLANT ADJUST ***** delay(100); } void timer1_interrupt(void) { int p; if(sSq == 3) { if(mode == 1) { if(ti < 160) { p = (160 * line) + ti; if(aRf == 0) { setFreq(1500 + 3.13 * frameBuf[p]); } else { setFreq(oFrq - (1500 + 3.13 * frameBuf[p])); } } else if(ti == 160) { line++; sSq = 2; } if(line == 120) { sSq = 0; } ti++; } else if(mode == 0) { if(line >= 40 && line <= 72) { if(ti < 160) { p = 160 * (line - 40) + ti; if(aRf == 0) { if(frameBuf[p]) { setFreq(2300); } else { setFreq(1500); } } else { if(frameBuf[p]) { setFreq(oFrq - 2300); } else { setFreq(oFrq - 1500); } } } else if(ti == 160) { line++; sSq = 2; } } else { if(aRf == 0) { switch(ti) { case 0: setFreq(1500); break; case 40: setFreq(1767); break; case 80: setFreq(2033); break; case 120: setFreq(2300); break; case 160: line++; if(line == 120) { sSq = 0; } else { sSq =2; } break; } } else { switch(ti) { case 0: setFreq(oFrq - 1500); break; case 40: setFreq(oFrq - 1767); break; case 80: setFreq(oFrq - 2033); break; case 120: setFreq(oFrq - 2300); break; case 160: line++; if(line == 120) { sSq = 0; } else { sSq =2; } break; } } } ti++; } } } void loop() { int x,y,i,p; if(sSq == 0) { setFreq(2); while(sSq == 0) { if(digitalRead(47) == 0) // - SEND SW - { sSq = 1; } else { delay(30); } } } else if(sSq == 1) { boolean err; if(digitalRead(49) == 1) // - MODE SW - { err = 0; mode = 1; //PICTURE MODE myFile = SD.open("sstv.bmp",FILE_READ) ; if(myFile) { 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 < 19200; i++) { frameBuf[i] = 0xFF; } x = 0; y = 0; addr = 0; rgbCnt = 0; while(myFile.available()) { bData = myFile.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 != 160) //PICTURE WIDTH { Serial.println("ERROR: PICTURE WIDTH MUST BE 160 PX"); err = 1; goto fileCls; } } else if(addr == 0x13) { if(bData != 0) //HIGHER BIT { Serial.println("ERROR: PICTURE WIDTH MUST BE 160 PX"); err = 1; goto fileCls; } } else if(addr == 0x16) { if(bData != 120) //PICTURE HEIGHT { Serial.println("ERROR: PICTURE HEIGHT MUST BE 120 PX"); err = 1; goto fileCls; } } else if(addr == 0x17) //HIGHER BIT { if(bData != 0) { Serial.println("ERROR: PICTURE HEIGHT MUST BE 120 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 = ((119 - y) * 160) + x; frameBuf[p] = pxGr_; x++; if(x == 160) { y++; x = 0; } if(y == 120) { goto fileCls; } if(cDepth == 32) { rgbCnt++; } else { rgbCnt = 0; } break; case 3: rgbCnt = 0; //-- RESERVE -- break; } } addr++; } fileCls: myFile.close(); } else { Serial.println("ERROR: CAN'T OPEN FILE") ; err = 1; } if(err == 1) { digitalWrite(31, HIGH); //ERROR LAMP while(true); //STOP } else { Serial.println("BMP FILE PARSED") ; } } else { mode = 0; //TEXT MODE for(p = 0; p < 5280; p++) //160*33 { frameBuf[p] = 0xFF; } for(i = 0; i < 27; i++) { byte fontNumber; char ch; if(i < 9) { ch = line00[i]; } else if(i < 18) { ch = line01[i - 9]; } else { ch = line02[i - 18]; } for(y = 0; y < 11; y++) { for(x = 0; x < 8; x++) { if(i < 9) { p = 8 + (160 * y) + (2 * (8 * i)) + (2 * x); //Width: x2 } else if(i < 18) { p = 8 + (160 * 11) + (160 * y) + (2 * (8 * (i - 9))) + (2 * x); //Width: x2 } else { p = 8 + (160 * 22) + (160 * y) + (2 * (8 * (i - 18))) + (2 * x); //Width: x2 } uint8_t mask; mask = pow(2, 7 - x); if(ch >= 65 && ch <= 90) //A to Z { fontNumber = ch - 65; } else if(ch >= 48 && ch <= 57) //0 to 9 { fontNumber = ch - 22; } else if(ch == '/'){fontNumber = 36;} else if(ch == '-'){fontNumber = 37;} else if(ch == '.'){fontNumber = 38;} else if(ch == '?'){fontNumber = 39;} else if(ch == '!'){fontNumber = 40;} else if(ch == ':'){fontNumber = 41;} else if(ch == ' '){fontNumber = 42;} else {fontNumber = 42;} if((fonts[fontNumber][y] & mask) != 0) { frameBuf[p ] = 0x00; frameBuf[p + 1] = 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 //RF OUTPUT { //--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; } else if(sSq == 2) { //--sync if(aRf == 0) { setFreq(1200); delayMicroseconds(10000); } else { setFreq(oFrq - 1200); delayMicroseconds(10000); } //-- ti = 0; sSq = 3; } }