/********************************************************
SSTV WIRELESS CAMERA @ToriSaanP
PD50 - GPS version - 2021.04.21 v1.32
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"
#include <Adafruit_GPS.h>
//arduino TX2 - GPS module RX
//arduino RX2 - GPS module TX
File myFile1;
#define chipSelect 4
#define CLK 6
#define FQ 7
#define DATA 8
#define RST 9
char fileName[8];
byte fileNumber;
byte sortBuf[15360]; //320(px)*16(lines)*3(bytes) R,G,B
byte yuvBuf[10240]; //320(px)*8(pairs)*4(bytes) Y0,RY,BY,Y1
volatile byte sTimes;
volatile byte sSq;
volatile int line;
volatile int ti;
volatile long syncTime;
volatile byte lineBuf[1280];
char info1[13] = "JI3BNB LIVE "; // ***** INFORMATION HEADER: MAX 12 CAHARCTERS *****
char info2[13]; //GPS
char info3[13]; //GPS
boolean vox = 1; //VOX TONE ENABLE
boolean colorBar = 1; //COLOR BAR ENABLE
boolean gps = 1; //GPS ENABLE
boolean test = 0; //TEST PATTERN
#define GPSECHO true
#define GPSSerial Serial2
Adafruit_GPS GPS(&GPSSerial);
uint32_t timer = millis();
//FONTS v1.1
const uint8_t fonts[45][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, 0x18, 0x18, 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, 0x3C, 0x24, 0x24, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //42:
{0x00, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, //43: '
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} //44: space
};
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); //For debug
Serial1.begin(38400); //Camera Serial
delay(300);
while(Serial.available())
{
ch = Serial.read(); //flush
}
while(Serial1.available())
{
ch = Serial1.read(); //flush
}
Serial.println("SERIAL READY");
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
GPS.sendCommand(PGCMD_ANTENNA);
delay(1000);
GPSSerial.println(PMTK_Q_RELEASE);
Serial.println("GPS READY");
pinMode(10, OUTPUT);
pinMode(31, OUTPUT); //LED(ERROR)
pinMode(33, OUTPUT); //LED(SHOT,SEND)
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(2500);
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)0x11); //320*240:
delay(100);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
//--cam compRatio
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x31);
Serial1.print((char)0x05);
Serial1.print((char)0x01);
Serial1.print((char)0x01);
Serial1.print((char)0x12);
Serial1.print((char)0x04);
Serial1.print((char)0x36); //SMALLER VALUE: LESS COMPRESSED (DEFAULT: 0x36)
delay(100);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
//--cam motionDetectEnable
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x42);
Serial1.print((char)0x03);
Serial1.print((char)0x00);
Serial1.print((char)0x01);
Serial1.print((char)0x01);
delay(100);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
Serial.println("JPEG CAMERA INITIALIZED");
Serial.println();
//PD90 532(uS/px)
Timer1.attachInterrupt(timer1_interrupt).start(286); // ***** PD50 286(uS/px) +/- SLANT ADJUST *****
delay(100);
}
void timer1_interrupt(void)
{
if(sSq == 3)
{
if(ti < 1280)
{
setFreq(1500 + 3.13 * lineBuf[ti]);
}
else if(ti == 1280)
{
setFreq(1200);
syncTime = micros();
sSq = 2;
}
ti++;
}
}
void jpegTake(void)
{
byte start_addr_m;
byte start_addr_l;
int byteCount;
byte incomingByte;
byte last2bytes[2];
boolean jpegEnd;
boolean err;
String string1 = String("S");
String string2 = String("");
String string3 = String(".JPG");
String string4 = String("");
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;
//--jpeg fileSave
string2 = String(fileNumber);
string4 = String(string1 + string2 + string3);
string4.toCharArray(fileName, 8);
err = false;
myFile1 = SD.open(fileName, FILE_WRITE | O_TRUNC);
if(myFile1)
{
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)
{
myFile1.write(incomingByte);
last2bytes[1] = incomingByte;
if(last2bytes[0] == 0xFF && last2bytes[1] == 0xD9)
{
/*
Serial.println("CAUGHT FFD9!");
*/
jpegEnd = true;
}
last2bytes[0] = last2bytes[1];
}
byteCount++;
}
/*
Serial.println();
*/
if(start_addr_l == 0xE0)
{
start_addr_l = 0x00;
start_addr_m += 0x01;
}
else
{
start_addr_l += 0x20; //DEC:32
}
}
}
else
{
Serial.println("CAN'T OPEN FILE");
err = true;
}
myFile1.close();
if(err == true)
{
digitalWrite(31, HIGH);
while(1);
}
else
{
Serial.println(fileName);
Serial.println("JPEG FILE SAVED");
Serial.println();
}
//--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();
}
void charToRAM(int infoNo)
{
int i,j,k,x,y;
int pxSkip;
for(i = 0; i < 12; i++)
{
byte fontNumber;
char ch;
switch(infoNo)
{
case 0:
ch = info1[i];
break;
case 1:
ch = info2[i];
break;
case 2:
ch = info3[i];
break;
}
for(y = 0; y < 11; y++)
{
for(x = 0; x < 8; x++)
{
pxSkip = 16 + (320 * (y + 3)) + (3 * 8 * i) + (3 * x); //Width: x3
uint8_t mask;
mask = pow(2, 7 - x);
fontNumber = 44; //default is SPACE
if(ch >= 65 && ch <= 90) //A to Z
{
fontNumber = ch - 65;
}
else if(ch >= 48 && ch <= 57) //0 to 9
{
fontNumber = ch - 22;
}
else
{
//Start from No. 36
byte specialChar[] = {'/', '-', '.', '?', '!', ':', '', '\'', ' '};
for(k = 0; k < 9; k++)
{
if(ch == specialChar[k])
{
fontNumber = k + 36;
}
}
}
if((fonts[fontNumber][y] & mask) != 0)
{
for(j = 0; j < 9; j++)
{
sortBuf[(3 * pxSkip) + j] = 0xFF;
}
}
}
}
}
}
void owColorBar(int lines)
{
int i;
for(i = 0; i < (320 * lines); i++)
{
if(i % 320 < 64) //Red
{
sortBuf[3 * i + 0] = 0xFF;
sortBuf[3 * i + 1] = 0x00;
sortBuf[3 * i + 2] = 0x00;
}
else if(i % 320 < 128) //Green
{
sortBuf[3 * i + 0] = 0x00;
sortBuf[3 * i + 1] = 0xFF;
sortBuf[3 * i + 2] = 0x00;
}
else if(i % 320 < 192) //Blue
{
sortBuf[3 * i + 0] = 0x00;
sortBuf[3 * i + 1] = 0x00;
sortBuf[3 * i + 2] = 0xFF;
}
else if(i % 320 < 256) //White
{
sortBuf[3 * i + 0] = 0xFF;
sortBuf[3 * i + 1] = 0xFF;
sortBuf[3 * i + 2] = 0xFF;
}
else if(i % 320 < 320) //Black
{
sortBuf[3 * i + 0] = 0x00;
sortBuf[3 * i + 1] = 0x00;
sortBuf[3 * i + 2] = 0x00;
}
}
}
void convToYUV(void)
{
int pxSkip;
int k,l;
for(k = 0; k < 8; k++) //8(pairs)
{
for(l = 0; l < 320; l++)
{
pxSkip = (320 * 2) * k; //320(px)x2(lines)
byte odR = sortBuf[(3 * pxSkip) + (3 * l) + 0];
byte odG = sortBuf[(3 * pxSkip) + (3 * l) + 1];
byte odB = sortBuf[(3 * pxSkip) + (3 * l) + 2];
byte evR = sortBuf[(3 * pxSkip) + (3 * (l + 320)) + 0];
byte evG = sortBuf[(3 * pxSkip) + (3 * (l + 320)) + 1];
byte evB = sortBuf[(3 * pxSkip) + (3 * (l + 320)) + 2];
float avR = (odR + evR) / 2.00;
float avG = (odG + evG) / 2.00;
float avB = (odB + evB) / 2.00;
float Y0 = 16.0 + (0.004 * (( 65.738 * odR) + (129.057 * odG) + ( 25.064 * odB)));
float Y1 = 16.0 + (0.004 * (( 65.738 * evR) + (129.057 * evG) + ( 25.064 * evB)));
float RY = 128.0 + (0.004 * ((112.439 * avR) + (-94.154 * avG) + (-18.285 * avB)));
float BY = 128.0 + (0.004 * ((-37.945 * avR) + (-74.494 * avG) + (112.439 * avB)));
pxSkip = (320 * 4) * k; //320(px)x4(bytes)
yuvBuf[pxSkip + (4 * l) + 0] = (byte)Y0;
yuvBuf[pxSkip + (4 * l) + 1] = (byte)RY;
yuvBuf[pxSkip + (4 * l) + 2] = (byte)BY;
yuvBuf[pxSkip + (4 * l) + 3] = (byte)Y1;
}
}
}
void fetchGPS(void)
{
String gpsStr1 = String(""); //34.47
String gpsStr2 = String(""); //N
String gpsStr3 = String(""); //135.31
String gpsStr4 = String(""); //E
String gpsStr5 = String(""); //58
String gpsLine1 = String("");
String gpsLine2 = String("");
int gpsFix = 0;
int notFix = 0;
while(1)
{
char c = GPS.read();
if(GPSECHO)
{
if(c)
{
Serial.print(c);
}
}
if(GPS.newNMEAreceived())
{
Serial.print(GPS.lastNMEA());
if(!GPS.parse(GPS.lastNMEA()))
{
//return;
}
}
if(millis() - timer > 2000)
{
timer = millis();
Serial.println();
Serial.print("Fix: "); Serial.println((int)GPS.fix);
if(GPS.fix)
{
Serial.print(GPS.latitude, 0); Serial.print(GPS.lat); Serial.print(", ");
Serial.print(GPS.longitude, 0); Serial.println(GPS.lon);
Serial.print("Altitude: "); Serial.println((int)GPS.altitude);
Serial.print("Satellites: "); Serial.println((int)GPS.satellites);
Serial.println();
gpsFix++;
if(gpsFix == 5)
{
//Latitude
gpsStr1 = String(GPS.latitude / 100, 2);
if(GPS.latitude / 100 < 10)
{
gpsStr1 = String(" " + gpsStr1);
}
gpsStr2 = String(GPS.lat); //N or S
//Upper line
gpsStr1.replace('.', ''); gpsStr1.concat('\'');
gpsLine1 = String(" " + gpsStr1 + gpsStr2 + " ALT");
gpsLine1.toCharArray(info2, 13);
//Longitude
gpsStr3 = String(GPS.longitude / 100, 2);
if(GPS.longitude / 100 < 10)
{
gpsStr3 = String(" " + gpsStr3);
}
else if(GPS.longitude / 100 < 100)
{
gpsStr3 = String(" " + gpsStr3);
}
gpsStr4 = String(GPS.lon); //E or W
//Altitude
gpsStr5 = String((int)GPS.altitude);
if(GPS.altitude == 0.00)
{
gpsStr5 = String("---");
}
else if(GPS.altitude < 10)
{
gpsStr5 = String(" " + gpsStr5);
}
else if(GPS.altitude < 100)
{
gpsStr5 = String(" " + gpsStr5);
}
else if(GPS.altitude >= 1000)
{
gpsStr5 = String("OVR");
}
//Lower line
gpsStr3.replace('.', ''); gpsStr3.concat('\'');
gpsLine2 = String(gpsStr3 + gpsStr4 + " " + gpsStr5);
gpsLine2.toCharArray(info3, 13);
Serial.println(gpsLine1);
Serial.println(gpsLine2);
Serial.println();
break;
}
}
else
{
notFix++;
if(notFix == 7)
{
gpsLine1 = String("GPS: ");
gpsLine2 = String("TIMEOUT ");
gpsLine1.toCharArray(info2, 13);
gpsLine2.toCharArray(info3, 13);
Serial.println(gpsLine1);
Serial.println(gpsLine2);
Serial.println();
break;
}
}
}
}
}
void jpegDecode(void)
{
char str[100];
uint8 *pImg;
int x,y,bx,by;
boolean err;
int i,j,k;
int pxSkip;
// Decoding start
JpegDec.decode(fileName,0);
//JpegDec.decode("SSTV.JPG",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("");
// Serial output
sprintf(str,"#SIZE,%d,%d",JpegDec.width,JpegDec.height);
Serial.println(str);
// Image size error
if(JpegDec.width != 320 || JpegDec.height != 240)
{
digitalWrite(31, HIGH);
Serial.println("ERROR: PICTURE SIZE SHOULD BE 320*240") ;
while(1);
}
// Header - Color bar - Decode jpeg - GPS position
err = false;
myFile1 = SD.open("YUV.DAT", FILE_WRITE | O_TRUNC);
if(myFile1)
{
// Add header 16(lines)
for(i = 0; i < 5120; i++) //320(px)x16(lines)
{
//Background color
sortBuf[3 * i + 0] = 0x00; //R
sortBuf[3 * i + 1] = 0x00; //G
sortBuf[3 * i + 2] = 0x00; //B
}
charToRAM(0);
//Now complete 16(lines) RGB header data available on sortBuf[]
//Convert to YUV and save to SD card
convToYUV();
for(k = 0; k < 10240; k++) //320(px)x4(bytes)x8(pairs)
{
myFile1.write(yuvBuf[k]);
}
// Decode jpeg
i = 0;
j = 0;
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);
}
else // RGB
{
//sprintf(str,"#RGB,%d,%d,%u,%u,%u", x, y, pImg[0], pImg[1], pImg[2]);
//Serial.println(str);
pxSkip = ((y - (16 * j)) * 320) + x;
sortBuf[(3 * pxSkip) + 0] = pImg[0];
sortBuf[(3 * pxSkip) + 1] = pImg[1];
sortBuf[(3 * pxSkip) + 2] = pImg[2];
i++;
if(i == 5120) //320(px)x16(lines)
{
//Now complete 16(lines) RGB image data available on sortBuf[]
//Overwrite color bar
if(colorBar)
{
if(j == 0)
{
owColorBar(6);
}
}
//Test pattern
if(test)
{
owColorBar(16);
}
//GPS position overlay
if(gps == true)
{
if(j == 12 || j == 13)
{
//Adjust background brightness
for(k = 0; k < 15360; k++)
{
sortBuf[k] -= 0x7F;
if(sortBuf[k] > 0x80)
{
sortBuf[k] = 0x00;
}
}
}
switch(j)
{
case 12:
charToRAM(1);
break;
case 13:
charToRAM(2);
break;
}
}
//Convert to YUV and save to SD card
convToYUV();
for(k = 0; k < 10240; k++) //320(px)x4(bytes)x8(pairs)
{
myFile1.write(yuvBuf[k]);
}
i = 0;
j++; //15(sections)
}
}
}
pImg += JpegDec.comps ;
}
}
}
//for(;;);
Serial.println("JPEG FILE DECODED");
Serial.println();
}
else
{
Serial.println("CAN'T OPEN FILE");
err = true;
}
myFile1.close();
if(err == true)
{
digitalWrite(31, HIGH);
while(1);
}
else
{
Serial.println("DAT FILE SAVED");
Serial.println();
}
}
void waitForStart(void)
{
byte incomingByte;
setFreq(2); //Mute
while(sSq == 0)
{
if(digitalRead(47) == 0) //TX enabled
{
if(digitalRead(49) == 0) //Holded
{
sTimes = 0;
sSq = 1;
}
else if(sTimes > 0) //Send 2 times
{
sSq = 1;
}
else
{
delay(500);
//Motion detection
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x37);
Serial1.print((char)0x01);
Serial1.print((char)0x01); //Start
delay(100);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
Serial.println("MOTION DETECTION STARTED");
Serial.println();
while(sSq == 0)
{
while(Serial1.available())
{
incomingByte = Serial1.read();
if(incomingByte == 0x39)
{
Serial.println(incomingByte, HEX);
Serial.println("MOTION DETECTED!");
Serial.println();
sTimes = 2;
sSq = 1;
}
}
if(analogRead(3) > 850) //External trigger
{
Serial.println("A3 TRIGGERED!");
Serial.println();
sTimes = 2;
sSq = 1;
}
delay(500);
if(digitalRead(49) == 0) //Holded
{
sTimes = 0;
sSq = 1;
}
else if(digitalRead(47) == 1) //TX disabled
{
sTimes = 0;
sSq = 0;
break;
}
}
//Motion detection
Serial1.print((char)0x56);
Serial1.print((char)0x00);
Serial1.print((char)0x37);
Serial1.print((char)0x01);
Serial1.print((char)0x00); //Stop
delay(100);
while(!Serial1.available()){ }
while(Serial1.available())
{
Serial.print(Serial1.read(), HEX);
Serial.print(" ");
}
Serial.println();
Serial.println("MOTION DETECTION STOPPED");
Serial.println();
}
}
else //Disabled
{
sTimes = 0;
}
}
}
void sstvEncode(void)
{
boolean err;
boolean head;
int i,j;
err = false;
myFile1 = SD.open("YUV.DAT");
if(myFile1)
{
head = true;
while(myFile1.available())
{
//Header
if(head == true)
{
digitalWrite(33, HIGH);
//-- 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 PD50 is B1011101 (DECIMAL 93)
setFreq(1900);
delay(300);
setFreq(1200); //BREAK
delay(10);
setFreq(1900);
delay(300);
setFreq(1200); //START BIT
delay(30);
setFreq(1100); //BIT 0 (LSB FIRST)
delay(30);
setFreq(1300); //BIT 1
delay(30);
setFreq(1100); //BIT 2,3,4
delay(90);
setFreq(1300); //BIT 5
delay(30);
setFreq(1100); //BIT 6
delay(30);
setFreq(1100); //EVEN PARITY
delay(30);
setFreq(1200); //STOP BIT
delay(30);
//-- VIS DONE --
/*
//VIS CODE for PD90 is B1100011 (DECIMAL 99)
setFreq(1900);
delay(300);
setFreq(1200); //BREAK
delay(10);
setFreq(1900);
delay(300);
setFreq(1200); //START BIT
delay(30);
setFreq(1100); //BIT 0,1 (LSB FIRST)
delay(60);
setFreq(1300); //BIT 2,3,4
delay(90);
setFreq(1100); //BIT 5,6
delay(60);
setFreq(1300); //EVEN PARITY
delay(30);
setFreq(1200); //STOP BIT
delay(30);
//-- VIS DONE --
*/
Serial.println("ENCODE!");
line = 0;
head = false;
//Sync
setFreq(1200);
syncTime = micros();
}
//Read one section (= two lines) data at a time
for(i = 0; i < 320; i++)
{
for(j = 0; j < 4; j++)
{
lineBuf[i + 320 * j] = myFile1.read();
}
}
//Serial.println(micros() - syncTime); //Check reading time
while(micros() - syncTime < 19200){} // ***** 20,000(uS) +/- ODD/EVEN LINE ADJUST *****
//Porch
setFreq(1500);
syncTime = micros();
while(micros() - syncTime < 2080){}
//Scan
ti = 0; sSq = 3;
while(sSq == 3) {};
line++;
Serial.print('.');
if(line % 64 == 0)
{
Serial.println();
}
if(line == 128)
{
Serial.println();
Serial.println("PICTURE SENT") ;
Serial.println(sTimes);
Serial.println();
digitalWrite(33, LOW);
setFreq(2);
sSq = 0;
if(sTimes > 0)
{
sTimes--;
}
else
{
int interval = 10; // ***** INTERVAL (IF NECESSARY) *****
Serial.print("INTERVAL ");
Serial.print(interval);
Serial.println("S");
j = 0;
for(i = 0; i < interval; i++)
{
delay(1000);
Serial.print('.');
j++;
if(j % 60 == 0)
{
Serial.println();
}
}
Serial.println();
}
}
}
}
else
{
Serial.println("CAN'T OPEN FILE");
err = true;
}
myFile1.close();
if(err == true)
{
digitalWrite(31, HIGH);
while(1);
}
}
void loop()
{
if(sSq == 0)
{
waitForStart();
}
else if(sSq >= 1)
{
if(gps)
{
fetchGPS();
}
jpegTake();
jpegDecode();
fileNumber++;
if(fileNumber == 100)
{
fileNumber = 0;
}
sstvEncode();
}
}