This example demonstrates ES Script capabilities when interfacing AD-7732 UART camera module. Source may be directly copied into ES Scripting console, and executed from there.
Sample hardware config used one AD-7732 module connected to FT232H interface board.
Cam module is a crap, but it's working crap. I managed to get only 640x480 frames, though programmer's guide stated otherwise, and smaller frames may be supported.
I did not get it working in non-packet transfer mode. The demo code contains quite comprehensive Camera class implementation + actual code to get frames from it, and save them as sequentially named files in User's document directory.
Camera module UART interface gets the baud rate from the first sync sequence after it's being powered. Though guide states, that baud change command is supported, it seem to break every other command which may follow. For instance, start in 9600, then set bau to 115200, the command gets proper ACK, then we change control uart baud to the new one, get proper sync on new baudrate OK.
But, commands issued afterwards baudrate is changed, never get any response. If anyone wants to experiment with this module, you may try normal COM port, or, maybe, virtual, based on chip from another manufacturer.
Demo ESS code for UART camera module frame reading. Click to unfold...
// AIDEVISION AD-7732 UART CAMERA DEMO
//
const Ad7732_cmdlen = 6;
const Ad7732_cmdhdr = 0xAA;
const Ad7732syncMaxRetries = 24;
const Ad7732baudDefault = 9600;
const Ad7732packetMaxLen = 512;
const Ad7732packetTmo = 100;
const Ad7732packetDataMaxLen = 506; //< 512-6
enum Ad7732cmd {
SYNC = 0x0D;
ACK = 0x0E;
NACK = 0x0F;
BAUDSET = 0x07;
RESET = 0x08;
PWRDOWN = 0x09;
INIT = 0x01;
PIC_GET = 0x04;
SNAPSHOT = 0x05;
PKG_SIZE = 0x06;
PIC_DATA = 0x0A;
}
enum Ad7732err {
PIC_TYPE = 0x01, "Picture Type error";
PIC_UPSCALE = 0x02, "Picture Up Scale";
PIC_SCALE = 0x03, "Picture Scale error";
UNEXPECTED_RESP = 0x04, "Unexpected Response";
PIC_SEND_TMO = 0x05, "Picture Send Timeout";
UNEXPECTED_CMD = 0x06, "Unexpected Command";
ASRAM_JPEG_TYPE = 0x07, "ASRAM JPEG Type error";
ASRAM_JPEG_SIZE = 0x08, "ASRAM JPEG Size error";
PIC_FMT = 0x09, "Picture Format error";
PIC_SIZE = 0x0A, "Picture Size error";
PARAM = 0x0B, "Parameter error";
SENDREG_TMO = 0x0C, "Send Register Timeout";
CMD_ID = 0x0D, "Command ID error";
PIC_NOT_READY = 0x0F, "Picture not ready";
TRANSFER_PKG_NO = 0x10, "Transfer Package Number error";
TRANSFER_PKG_SIZE = 0x11, "Wrong Transfer Package Size";
CMD_HEADER = 0xF0, "Command Header error";
CMD_LENGTH = 0xF1, "Command Length error";
PIC_SEND = 0xF5, "Picture Send error";
CMD_SEND = 0xFF, "Command Send error";
}
enum Ad7732format {
GreyScale2bit = 0x01;
GreyScale4bit = 0x02;
GreyScale8bit = 0x03;
Color2bit = 0x05;
Color16bit = 0x06;
JPEG = 0x07;
}
enum Ad7732previewSize {
_80x60 = 0x01;
_160x120 = 0x03;
}
enum Ad7732jpegSize {
_80x64 = 0x01;
_160x128 = 0x03;
_320x240 = 0x05;
_640x480 = 0x07;
}
enum Ad7732picSrc {
Snapshot = 0x01;
Preview = 0x02;
JPEG = 0x03;
};
enum Ad7732snapshot {
Compressed = 0;
Uncompressed = 1;
}
const Ad7732bauds = [
[
7200,
9600,
14400,
19200,
28800,
38400,
57600,
115200
],
[
[0xFF, 0x01],
[0xBF, 0x01],
[0x7F, 0x01],
[0x5F, 0x01],
[0x3F, 0x01],
[0x2F, 0x01],
[0x1F, 0x01],
[0x0F, 0x01]
]
];
object Ad7732
{
// State vars
var m_uart,
m_rdBuff,
m_wrBuff,
m_synched;
new()
{
m_synched = false;
}
// Services
//
function uartBaudReset(baud)
{
EsScriptDebug::log("uartBaudReset(%d)", baud);
m_uart.close();
m_uart$baud = baud;
m_uart.open();
}
function buffRead(len)
{
m_rdBuff = m_uart.bytesGet(
len, //< Read len bytes
Ad7732packetTmo+2*m_uart.txTimeEstimateGet(len) //< in XXX ms
);
/* if( m_rdBuff )
{
if( m_rdBuff#countGet() < 32 )
EsScriptDebug::log("buffRead: %s", m_rdBuff);
else
EsScriptDebug::log("buffRead: %s + %d more bytes...", m_rdBuff#sliceGet(0, 32), m_rdBuff#countGet()-32);
} */
}
function responseHandle(expectedCmdId)
{
if(Ad7732cmd$$ACK == m_rdBuff[1])
return (expectedCmdId == m_rdBuff[2]);
if( Ad7732cmd$$NACK == m_rdBuff[1] )
{
if( m_rdBuff[4] in Ad7732err )
throw EsStr::format(
"NACK response to AD7732 %s command, reason: %s",
Ad7732cmd.valueSymbolGet(expectedCmdId),
Ad7732err.valueLabelGet(m_rdBuff[4])
);
else
throw EsStr::format(
"NACK response to AD7732 %s command, unknown error code",
Ad7732cmd.valueSymbolGet(expectedCmdId)
);
}
else
throw EsStr::format(
"Corrupt response to AD7732 %s command",
Ad7732cmd.valueSymbolGet(expectedCmdId)
);
}
// Command read-write helpers
function cmdRead(cmdId)
{
buffRead( Ad7732_cmdlen );
return !m_rdBuff#isEmpty() &&
(Ad7732_cmdlen == m_rdBuff#countGet()) &&
(Ad7732_cmdhdr == m_rdBuff[0]) &&
(cmdId == m_rdBuff[1]);
}
function cmdWrite(cmdId, param1, param2, param3, param4)
{
m_wrBuff = B"";
m_wrBuff += Ad7732_cmdhdr#asByte();
m_wrBuff += cmdId#asByte();
m_wrBuff += param1#asByte();
m_wrBuff += param2#asByte();
m_wrBuff += param3#asByte();
m_wrBuff += param4#asByte();
// EsScriptDebug::log("cmdWrite: %s", m_wrBuff);
return (Ad7732_cmdlen == m_uart.bytesPut( m_wrBuff, 0 ));
}
function cmdWriteAndReadResponse( cmdId, param1, param2, param3, param4 )
{
if( cmdWrite(cmdId, param1, param2, param3, param4) )
{
buffRead( Ad7732_cmdlen );
if( m_rdBuff#isEmpty() || (Ad7732_cmdlen > m_rdBuff#countGet()) )
throw EsStr::format(
"Could not get response to command %s",
Ad7732cmd.valueSymbolGet(cmdId)
);
if( Ad7732_cmdhdr != m_rdBuff[0] )
throw EsStr::format(
"Unknown command %s response header: 0x%02X, expected 0xAA",
Ad7732cmd.valueSymbolGet(cmdId),
m_rdBuff[0]
);
return responseHandle( cmdId );
}
return false;
}
// Commands implementation
//
function synch()
var cnt = 0, cntack;
{
m_synched = false;
while( cnt++ < Ad7732syncMaxRetries && cmdWrite(Ad7732cmd$$SYNC, 0,0,0,0) )
{
if(
cmdRead(Ad7732cmd$$ACK) &&
Ad7732cmd$$SYNC == m_rdBuff[2]
)
{
cntack = m_rdBuff[3];
if(
cmdRead( Ad7732cmd$$SYNC ) &&
cmdWrite( Ad7732cmd$$ACK, Ad7732cmd$$SYNC, cntack, 0, 0 )
)
{
m_synched = true;
break;
}
}
}
return m_synched;
}
function baud(val)
var baudPrev, baudItem;
{
if( !m_uart.isRateSupported(val) )
return false;
baudItem = Ad7732bauds[0]#find(val);
if( baudItem#isEmpty() )
throw EsStr::format(
"Baud rate %d is not supported by AD7732",
val
);
if(
cmdWrite(Ad7732cmd$$BAUDSET, Ad7732bauds[1][baudItem][0], Ad7732bauds[1][baudItem][1], 0, 0)
)
{
baudPrev = m_uart$baud;
uartBaudReset(val);
if( cmdRead(Ad7732cmd$$ACK) &&
Ad7732cmd$$BAUDSET == m_rdBuff[2]
)
return true;
else
uartBaudReset(baudPrev);
}
return false;
}
function highestBaudNegotiate()
var adbaud, adbauds = Ad7732bauds[0];
{
adbauds#sortDescending();
foreach( adbaud in adbauds )
{
if( baud( adbaud ) )
return true;
}
return false;
}
function baudScan()
var adbaud, adbauds = Ad7732bauds[0];
{
adbauds#sortDescending();
foreach( adbaud in adbauds )
{
uartBaudReset(adbaud);
if( synch() )
return true;
}
return false;
}
function reset()
{
// NB! no ACK is sent in response to RESET
if( cmdWrite(Ad7732cmd$$RESET, 1, 0, 0, 0) )
return synch();
return false;
}
function reboot()
{
// NB! no ACK is sent in response to RESET
if( cmdWrite(Ad7732cmd$$RESET, 0, 0, 0, 0) )
return synch();
return false;
}
function powerDown()
{
return cmdWrite(Ad7732cmd$$PWRDOWN, 0, 0, 0, 0) &&
cmdRead(Ad7732cmd$$ACK) &&
Ad7732cmd$$PWRDOWN == m_rdBuff[2];
}
function init(color, previewSize, jpegSize)
{
if( !(color in Ad7732format) )
throw "Unsupported AD7732 color format";
if( !(previewSize in Ad7732previewSize) )
throw "Unsupported AD7732 preview size";
if( !(jpegSize in Ad7732jpegSize) )
throw "Unsupported AD7732 JPEG size";
if( cmdWrite(Ad7732cmd$$INIT, 0, color, previewSize, jpegSize) )
{
buffRead( Ad7732_cmdlen );
if(
!m_rdBuff#isEmpty() &&
(Ad7732_cmdlen == m_rdBuff#countGet()) &&
(Ad7732_cmdhdr == m_rdBuff[0])
)
return responseHandle( Ad7732cmd$$INIT );
}
return false;
}
function snapshot(type, skipFrames)
{
if( !(type in Ad7732snapshot) )
throw "Unknown snapshot compression type specified";
return cmdWriteAndReadResponse(
Ad7732cmd$$SNAPSHOT,
type,
(skipFrames & 0xFF),
(skipFrames >> 8) & 0xFF,
0
);
}
function packetSizeSet(size)
{
return cmdWriteAndReadResponse(
Ad7732cmd$$PKG_SIZE,
0x08,
(size & 0xFF),
(size >> 8) & 0xFF,
0
);
}
function pictureRequest(src)
var len = 0;
{
if( !(src in Ad7732picSrc) )
throw "Unknown picture source specified in picture request";
if( cmdWriteAndReadResponse( Ad7732cmd$$PIC_GET, src, 0, 0, 0 ) )
{
if( !cmdRead( Ad7732cmd$$PIC_DATA ) || (src != m_rdBuff[2]) )
throw "Could not get picture data length packet";
len = m_rdBuff[3] + (m_rdBuff[4] << 8) + (m_rdBuff[5] << 16);
}
return len;
}
function pictureGet(src, packetSize)
var len, packetDataLen, packetLen = Ad7732packetMaxLen,
packetDataMaxLen = Ad7732packetDataMaxLen,
packetIdx = 0, idx, curIdx, crc, packetCrc, result = B"";
{
if( packetSize )
{
if( packetSize < 16 )
throw EsStr::format(
"Invalid transfer packet size %d requested. Must be at least 16",
packetSize
);
if( !packetSizeSet(packetSize) )
throw EsStr::format(
"Could not set-up transfer packet size %d",
packetSize
);
packetLen = packetSize;
packetDataMaxLen = packetLen-6;
}
len = pictureRequest(src);
while( len > 0 )
{
if( cmdWrite(Ad7732cmd$$ACK, 0, 0, packetIdx & 0xFF, (packetIdx >> 8) & 0xFF) )
{
buffRead(packetLen);
if(
!m_rdBuff#isEmpty() &&
packetLen == m_rdBuff#countGet()
)
{
curIdx = m_rdBuff[0] + (m_rdBuff[1] << 8);
if( packetIdx != curIdx )
throw EsStr::format(
"Unexpected packet index: %d, expected: %d",
curIdx,
packetIdx
);
packetDataLen = m_rdBuff[2] + (m_rdBuff[3] << 8);
if( packetDataLen > (packetLen-6) )
throw EsStr::format(
"Invalid packet data length %d, must be less than, or equal to %d",
packetDataLen,
packetLen-6
);
packetCrc = m_rdBuff[packetLen-2]; //< PG - only low byte matters
// Calculate checksum 8 mod 256
crc = 0;
for( idx = 0; idx < packetLen-2; ++idx )
crc += m_rdBuff[idx];
crc &= 0xFF;
if( crc != packetCrc )
throw EsStr::format(
"Invalid CRC 0x%0X for packet %d, expected 0x%0X",
crc,
curIdx,
packetCrc
);
result += m_rdBuff#sliceGet(
4,
packetDataLen+4
);
len -= packetDataLen;
if( len < packetDataMaxLen )
packetLen = len+6;
EsScriptDebug::log("Read %d image bytes, %d remaining", packetDataLen, len);
++packetIdx;
}
else
throw EsStr::format(
"Could not read packet %d",
packetIdx
);
}
}
// Last ACK
if( !cmdWrite(Ad7732cmd$$ACK, 0, 0, 0xF0, 0xF0) )
throw "Could not write the last ACK in packet mode";
return result;
}
// Properties
//
property uart;
read: { return m_uart; }
write: {
if( !__value#isEmpty() && !__value#isKindOf("EsChannelIoUart") )
throw EsStr::format(
"Could not assign instance of incompatible type '%s' to Ad7732$uart property",
__value$type
);
m_uart = __value;
}
property synched;
read: { return m_synched; }
};
// Demo code
//
var imgCnt, picdata, cam = new Ad7732(),
uarts = EsChannelIoUart::enumerate(
false, // Do not include busy ports
false // Enumerate USB only devices
),
chnl = new EsChannelIoUart(),
file;
if( uarts#countGet() < 1 )
throw "Could not find any free USB UARTs";
// Print out the first found UART info
EsScriptDebug::log(
"UART Channel on '%s' is selected for IO",
uarts[0]
);
chnl$baud = 115200;
chnl$port = uarts[0][0];
try
{
if( chnl.open() )
cam$uart = chnl;
else
EsScriptDebug::log("Could not open IO channel");
EsScriptDebug::log("Synching with Camera...");
if( cam.synch() )
EsScriptDebug::log("Camera SYNCed OK");
else
{
EsScriptDebug::log("Camera SYNC FAILED, scanning for current BAUD");
if( cam.baudScan() )
EsScriptDebug::log("Camera BAUD SCAN found pre-set baud %s", cam$uart$baud);
else
{
EsScriptDebug::log("Camera BAUD SCAN failed");
return;
}
}
cam.reset();
if( cam.init( Ad7732format$$JPEG, Ad7732previewSize$$_80x60, Ad7732jpegSize$$_640x480 ) )
EsScriptDebug::log("Camera Initialized OK");
else
{
EsScriptDebug::log("Camera Initialization FAILED");
return;
}
/* cam.snapshot(
Ad7732snapshot$$Uncompressed,
0
);
*/
imgCnt = 0;
while( true )
{
// Get JPEG preview
picdata = cam.pictureGet(Ad7732picSrc$$Preview, 4096);
file = new EsFile(
EsPath::stdDocsGet() + "/image" + imgCnt + ".jpg",
EsFileFlag$$Write
);
if( file.open() )
{
file.writeAllAsBinBuffer(picdata);
file.close();
}
++imgCnt;
}
}
catch
{
EsScriptDebug::log( __exception$reason );
chnl.close();
}