/********************************************************
JI3BNB SSTV ENCODER
Robot B/W8 Format, TEXT MESSAGE / JPEG CAMERA
2018.07.29 v2.0
ARDUINO "DUE" + ETHERNET SHIELD R3 + AD9850 DDS
ETHERNET SHIELD ACTS JUST AS A "SD CARD SHIELD"
--- THIS CODE IS IN THE PUBLIC DOMAIN ---
JPEG DECODE SECTION IS BASED ON MAKOTO KURAUCHI'S WORK
https://github.com/MakotoKurauchi/JPEGDecoder
/********************************************************/
#include <arduino.h>
#include <SPI.h>
#include <SD.h>
#include <DueTimer.h>
#include "JPEGDecoder.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); //--data
}
else
{
digitalWrite(DATA, LOW); //--data
}
shortPulse(CLK);
}
//--send rest 8bit
digitalWrite(DATA, LOW); //--data
for (int i=0; i<8; i++)
{
shortPulse(CLK);
}
//--finish
shortPulse(FQ);
}
void setup()
{
char ch;
Serial.begin(9600);
Serial1.begin(38400); //Camera Serial
delay(100);
while(Serial.available())
{
ch = Serial.read(); //flush
}
while(Serial1.available())
{
ch = Serial1.read(); //flush
}
Serial.println("SERIAL READY");
pinMode(10, OUTPUT);
pinMode(31, OUTPUT); //ERROR LAMP
pinMode(33, OUTPUT); //SHOT LAMP
digitalWrite(31, LOW);
digitalWrite(33, LOW);
if (!SD.begin(chipSelect))
{
Serial.println("ERROR: INSERT SD CARD AND RESTART");
digitalWrite(31, HIGH);
return ;
}
Serial.println("SD CARD INITIALIZED");
Serial.println();
pinMode(47, INPUT);
pinMode(49, INPUT);
digitalWrite(47, 1); //INTERNAL PULL UP
digitalWrite(49, 1); //INTERNAL PULL UP
pinMode(CLK, OUTPUT);
pinMode(FQ, OUTPUT);
pinMode(DATA, OUTPUT);
pinMode(RST, OUTPUT);
//--dds reset
shortPulse(RST);
shortPulse(CLK);
//--change mode
shortPulse(FQ);
//--cam reset
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x26);
Serial1.print((char)0x00);
delay(1900);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
//--cam setSize
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x54);
Serial1.print((char)0x01);
Serial1.print((char)0x22); //160*120
delay(100);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
Serial.println("JPEG CAMERA INITIALIZED");
Serial.println();
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 jpegTake(void)
{
byte start_addr_m;
byte start_addr_l;
boolean jpegEnd;
int jpegEndPos;
int byteCount;
byte incomingByte;
byte jpegArray[20001];
boolean err;
int i;
Serial.println();
Serial.println("NEW FILE");
//--cam takePhoto
digitalWrite(33, HIGH);
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x36);
Serial1.print((char)0x01);
Serial1.print((char)0x00);
delay(100);
while(!Serial1.available()){ }
digitalWrite(33, LOW);
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
//--cam readSize
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x34);
Serial1.print((char)0x01);
Serial1.print((char)0x00);
delay(100);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
//--cam readContent
start_addr_m = 0x00;
start_addr_l = 0x00;
jpegEnd = false;
i = 0;
while(!jpegEnd)
{
/*
Serial.print("start_addr: ");
Serial.print(start_addr_m, HEX);
Serial.print(" ");
Serial.print(start_addr_l, HEX);
Serial.println();
delay(10);
*/
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x32);
Serial1.print((char)0x0C);
Serial1.print((char)0x00);
Serial1.print((char)0x0A);
Serial1.print((char)0x00);
Serial1.print((char)0x00);
Serial1.print((char)start_addr_m); //MM
Serial1.print((char)start_addr_l); //MM
Serial1.print((char)0x00);
Serial1.print((char)0x00);
Serial1.print((char)0x00); //KK
Serial1.print((char)0x20); //KK (DEC:32)
Serial1.print((char)0x00); //XX
Serial1.print((char)0x0A); //XX
while(!Serial1.available()){ }
delay(22); //10ms at least
byteCount = 0;
while(Serial1.available())
{
incomingByte = Serial1.read();
/*
Serial.print(incomingByte, HEX);
Serial.print(" ");
*/
if(byteCount >=5 && byteCount <=36)
{
jpegArray[i] = incomingByte;
if(jpegArray[i - 1] == 0xFF && jpegArray[i] == 0xD9)
{
/*
Serial.println("CAUGHT FFD9!");
*/
jpegEndPos = i;
jpegEnd = true;
}
i++;
}
byteCount++;
}
/*
Serial.println();
*/
if(start_addr_l == 0xE0)
{
start_addr_l = 0x00;
start_addr_m += 0x01;
}
else
{
start_addr_l += 0x20; //DEC:32
}
}
//--cam stop
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x36);
Serial1.print((char)0x01);
Serial1.print((char)0x03);
delay(100);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
//--jpeg fileSave
err = false;
myFile = SD.open("SSTV.JPG", FILE_WRITE | O_TRUNC);
if(myFile)
{
for(i = 0; i <= jpegEndPos; i++)
{
myFile.write(jpegArray[i]);
}
}
else
{
Serial.println("CAN'T OPEN FILE");
err = true;
}
myFile.close();
if(err == true)
{
digitalWrite(31, HIGH);
while(1);
}
else
{
Serial.println("JPEG FILE SAVED");
Serial.println();
}
}
void jpegDecode(void)
{
char str[100];
char filename[] = "SSTV.JPG";
uint8 *pImg;
int x,y,bx,by;
float pxGr;
uint8_t pxGr_;
int i;
// Decoding start
JpegDec.decode(filename,0);
// Image Information
Serial.print("Width :");
Serial.println(JpegDec.width);
Serial.print("Height :");
Serial.println(JpegDec.height);
Serial.print("Components:");
Serial.println(JpegDec.comps);
Serial.print("MCU / row :");
Serial.println(JpegDec.MCUSPerRow);
Serial.print("MCU / col :");
Serial.println(JpegDec.MCUSPerCol);
Serial.print("Scan type :");
Serial.println(JpegDec.scanType);
Serial.print("MCU width :");
Serial.println(JpegDec.MCUWidth);
Serial.print("MCU height:");
Serial.println(JpegDec.MCUHeight);
Serial.println("");
// Output CSV
sprintf(str,"#SIZE,%d,%d",JpegDec.width,JpegDec.height);
Serial.println(str);
// Image size error
if(JpegDec.width != 160 || JpegDec.height != 120)
{
digitalWrite(31, HIGH);
Serial.println("ERROR: PICTURE SIZE SHOULD BE 160*120") ;
while(1);
}
// Clear buffer
for(i = 0; i < 19200; i++)
{
frameBuf[i] = 0xFF;
}
while(JpegDec.read())
{
pImg = JpegDec.pImage ;
for(by=0; by < JpegDec.MCUHeight; by++)
{
for(bx=0; bx < JpegDec.MCUWidth; bx++)
{
x = JpegDec.MCUx * JpegDec.MCUWidth + bx;
y = JpegDec.MCUy * JpegDec.MCUHeight + by;
if(x < JpegDec.width && y < JpegDec.height)
{
if(JpegDec.comps == 1) // Grayscale
{
//sprintf(str,"#RGB,%d,%d,%u", x, y, pImg[0]);
//Serial.println(str);
frameBuf[(160 * y) + x] = pImg[0];
}
else // RGB
{
//sprintf(str,"#RGB,%d,%d,%u,%u,%u", x, y, pImg[0], pImg[1], pImg[2]);
//Serial.println(str);
pxGr = ((pImg[0] * 0.30) + (pImg[1] * 0.59) + (pImg[2] * 0.11));
pxGr_ = (uint8_t)pxGr;
frameBuf[(160 * y) + x] = pxGr_;
}
}
pImg += JpegDec.comps ;
}
}
}
//for(;;);
Serial.println("JPEG FILE DECODED");
Serial.println();
}
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)
{
if(digitalRead(49) == 1) // - MODE SW -
{
mode = 1; //CAMERA MODE
jpegTake();
jpegDecode();
//--Adjust Brightness
for(p = 0; p < 19200; p++)
{
frameBuf[p] += 0x20;
if(frameBuf[p] < 0x20)
{
frameBuf[p] = 0xFF;
}
}
}
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
{
//--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;
}
}