[Tinyos-contrib-commits]
CVS: tinyos-1.x/contrib/handhelds/apps/FTP_DataLogger
FTP_DataLogger.nc, NONE, 1.1 FTP_DataLoggerM.nc, NONE,
1.1 Makefile, NONE, 1.1 README, NONE, 1.1 crc16.h, NONE,
1.1 shimmerDisk.c, NONE, 1.1 shimmerSerial.c, NONE,
1.1 shimmerSerial.h, NONE, 1.1
steve ayer
ayer1 at users.sourceforge.net
Thu Mar 27 13:05:15 PDT 2008
Update of /cvsroot/tinyos/tinyos-1.x/contrib/handhelds/apps/FTP_DataLogger
In directory sc8-pr-cvs10.sourceforge.net:/tmp/cvs-serv4716/FTP_DataLogger
Added Files:
FTP_DataLogger.nc FTP_DataLoggerM.nc Makefile README crc16.h
shimmerDisk.c shimmerSerial.c shimmerSerial.h
Log Message:
first check-in of stephen linder's serial data collection utility,
which was developed to work sensibly on a windows platform but should
work on linux.
extensive notes in README.
--- NEW FILE: FTP_DataLogger.nc ---
/*
* Copyright (c) 2007, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Stephen Linder
* August, 2007
*
* started from work by Jason Waterman during the summer of 2007
*/
configuration FTP_DataLogger {
}
implementation {
components Main,
FTP_DataLoggerM,
PowerSupplyMonitorC,
UserButtonC,
TimerC,
HPLUARTC,
HPLUSART0M,
LedsC,
DMA_M,
MMA7260_AccelM,
DS2411LiteM, // module \tos\system\DS2411LiteM.nc
SD_DMA_C;
// if you want to have the init, start and stop functions of the StdControl
// interface to be called automatically you must wire them here.
// Because the SD card interface was originally not wired I developed all the
// code with out using this feature SPL 2-25-08 Main.StdControl -> FTP_DataLoggerM;
Main.StdControl -> FTP_DataLoggerM;
Main.StdControl -> UserButtonC;
Main.StdControl -> TimerC;
FTP_DataLoggerM.PSMStdControl -> PowerSupplyMonitorC;
FTP_DataLoggerM.PowerSupplyMonitor -> PowerSupplyMonitorC;
FTP_DataLoggerM.Button -> UserButtonC.UserButton;
FTP_DataLoggerM.sampleTimer -> TimerC.Timer[unique("Timer")];
FTP_DataLoggerM.blinkRunAndPowerLEDTimer -> TimerC.Timer[unique("Timer")];
FTP_DataLoggerM.blinkGreenTimer -> TimerC.Timer[unique("Timer")];
FTP_DataLoggerM.IDChip -> DS2411LiteM.IDChip;
FTP_DataLoggerM.HPLUART -> HPLUARTC;
FTP_DataLoggerM.USARTControl -> HPLUSART0M;
FTP_DataLoggerM.Leds -> LedsC;
FTP_DataLoggerM.LocalTime -> TimerC;
FTP_DataLoggerM.SD -> SD_DMA_C;
FTP_DataLoggerM.DMA0 -> DMA_M.DMA[0];
FTP_DataLoggerM.AccelStdControl -> MMA7260_AccelM;
FTP_DataLoggerM.Accel -> MMA7260_AccelM;
}
--- NEW FILE: FTP_DataLoggerM.nc ---
/*
* Copyright (c) 2007, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the Intel Corporation nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
[...968 lines suppressed...]
result_t sendNextSerialByte() {
bool busy;
atomic {
busy = (bytePending == TRUE);
}
if (busy) {
return FAIL;
}
if (head == tail) {
return SUCCESS; // buffer is empty
}
atomic bytePending = TRUE;
call HPLUART.put(serialBuffer[tail]);
return SUCCESS;
}
async event void SD.available() {}
async event void SD.unavailable() {}
}
--- NEW FILE: Makefile ---
# $Id: Makefile,v 1.1 2008/03/27 20:05:13 ayer1 Exp $
#
# Copyright (c) 2007, Intel Corporation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# Neither the name of the Intel Corporation nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: Jason Waterman
# July, 2007
COMPONENT=FTP_DataLogger
PFLAGS=-DDEFAULT_BAUDRATE=115200
# PFLAGS += -g
include ../Makerules
--- NEW FILE: README ---
Data Logging to SD Card
Stephen Paul Linder
December 2007
spl at alum.mit.edu
The FTP_DataLogger TinyOS application for SHIMMER I logs data from the
three axis accelerometer to the SD card. Data is saved into 512-byte
blocks. Each block has 84-data sets, a time stamp, experimental ID
number and 16-bit CRC.
Data that has been saved on the SD card can be downloaded from the
SHIMMER over the USB connection to the docking station. Significantly
faster download speeds can be obtained by removing the SD card and
using a card reader for the data download. Every attempt has been made
to recover gracefully from intermittent faults writing to the SD card
because of mechanical vibration, etc.
SHIMMER2 should also support this application but you may want to use
the FAT_DataLogger application instead. It stores the data in a FAT
readable file.
Starting up
The SHIMMER is controlled using the /ss.exe / program compiled from the
/shimmerSerial.c/. It shares a /shimmerSerial.h/ header file with the
SHIMMER-side software. The /shimmerSerial.c/ was written under Cygwin
and should compile and run under Linux. Compile by using /gcc/
gcc shimmerSerial.c -o ss.exe
Place the SHIMMER in the dock station. Reset the SHIMMER and confirm
that the yellow LED is blinking. Confirm that the serial link to the
SHIMMER (thru the USB interface) is working by toggling the red LED on
the SHIMMER
./ss.exe d
The response to this command is
$ ./ss -d
Shimmer Serial version 1.00
Attempting port /dev/ttyS3
Port /dev/ttyS3 open to Shimmer.
Toggling red LED ...
Closing port
The SHIMMER docking station uses a FTDI chip to interface the SHIMMERs
serial port to the USB port. FTDI provides a device driver that maps the
USB port to a virtual serial port. / ss.exe/ will search through the
first ten serial ports to find the SHIMMER. Sometimes you may need to
specify the serial port. You can do this using /p/ option.
./ss.exe -p /dev/ttyS3 d
The /p/ option must be the first option.
Initiating Data Collection
Start data collection by placing the SHIMMER in the dock station. Reset
the SHIMMER and confirm that the yellow LED is blinking.
Run /ss.exe/ under Cygwin with the following option to commence data logging
./ss.exe -A "Shimmer ID String" // Linux
To run /ss.exe/ on a Windows PC without Cygqin, have the cyqwin1.dll in
the same subdirectory as ss.exe.
ss -A "Shimmer ID String"
When data is being collected the green LED blinks every time a data
block is saved to the SD card. Each block is save with a single time
stamp and an experimental ID.
Terminating Data Collection
Data collection can be terminated normally by inserting the SHIMMER in
the docking station and depressing the user button (the button furthest
from the USB connector). The LEDs then stop blinking and the green LED
remains on.
When data collection is done this way you will have a status
/Data collection completed normally/
Data collection will also terminate when the battery monitor detects a
low battery conditions. When this occurs the status becomes
/Shimmer powered down because of low battery voltage /
Unexpected termination either because of a hardware fault causing an
abnormal program termination or possible software error will result in
/Data collection terminated without setting status/
When this occurs SHIMMER is unable to save the number of blocks of data
collected on the header block of the SHIMMER. Therefore, /ss.exe/ must
use the experimental ID to search sequentially for all the data that was
logged during the current experiment. The experimental ID is saved with
every data block and consists of the lower sixteen bits of the starting
time of the experiment in seconds from the start of the UNIX epoch.
Downloading Data to PC
Data is downloaded using the /F/ option
$ ./ss -F >t.txt
A successful download produces the following output
$ ./ss -F >t.txt
Shimmer Serial version 1.00
Attempting port /dev/ttyS3
Port /dev/ttyS3 open to Shimmer.
Shimmer Serial version 1.00
Shimmer Type: 3-axis accel
Release Date: 14-Nov-07
Shimmer ID: a third turntable test
Shimmer Status: Data collection completed normally
Starting time of data collection: Tue Dec 18 20:06:46 2007
SHIMMER collected 0 minutes of data in 2 blocks.
/../
Closing port
/ /
During the download the green and orange on the SHIMMER blink with every
block downloaded. On the PC-side a . is printed for each block downloaded.
If data logging was terminated unexpectedly you will get the following
type of output
$ ./ss -F >t.txt
Shimmer Serial version 1.00
Attempting port /dev/ttyS3
Port /dev/ttyS3 open to Shimmer.
Shimmer Type: 3-axis accel
Release Date: 14-Nov-07
Shimmer ID: a third turntable test
Shimmer Status: Data collection terminated without setting status.
Starting time of data collection: Tue Dec 18 20:09:38 2007
SHIMMER collected 117440511 minutes of data in -1 blocks.
Header block indicates that the data collection was interrupted.
Retrieving data blocks that have a experimental ID of 28498 .
..
Block ID does not match, trying next block.
.
Block ID does not match, trying next block.
.
Block ID does not match, Stopping data dump.
Closing port
/ /
Finally you should ensure that the version of /.///ss/ is the same as
the software running on the SHIMMER. You will see an error message if
they are not the same. Running with different version might result in
the data saved on the SD card not being saved correctly. You will
receive the following error message
WARNING SOFTWARE VERSIONS DO NOT MATCH.
Shimmer firmware: 1.00, PC software: 1.10
Data fields are possibly misaligned. Check if data looks reasonable.
Data is not erased after download and can be downloaded multiple times.
The output file is space delimited and can be read by Matlab. Look at
plotData.m for an
example.
Column 1 time e.g. 1193179481.099
Column 2-4 raw data e.g. 2883 2276 2473
Increasing download speeds
The download speed obtained is quiet variable with the fastest speeds
obtained using Linux. Michael Healy was able to achieve the following
results downloading 10 minutes of data
* *
*10 minutes (366 blocks)*
*Linux*
*Cygwin*
*No USB Hub*
16s
571s
*IOGEAR GUH275 USB Hub(USB 2.0)*
16s
65s
When using Cygwin the data transfer is always faster when the docking
station is connected to a USB hub. This has been isolated to a problem
with Cygwin not Windows.
Downloading Data using a SD Card Reader
If your applications allows you to remove your SD card the data can be
downloaded significantly faster by removing the SD card and placing it
in a card reader. The program sd.exe can then be used to read the data.
Compile sd.exe be
$ gcc shimmerDisk.c -o sd
Usage is
./sd <devname>
Give the name of the device that has the SD card with the SHIMMER data
Example device names would be /dev/sdb for the second hard drive
/dev/sdc for the third hard drive
The SD card is accessed as an attached disk. The device letter for the
disk is usually less than the drive letter given to the reader by MS
Windows. If you inadvertently try to download your hard drive you will
get the following error message
$ ./sd /dev/sda >t.txt
Shimmer Disk version 1.00
Attempting to access /dev/sda
ERROR: bad header block. Try again.
While every effort has been made to keep the ss.exe and sd.exe
synchronized they may respond differently to error conditions on the SD
card.
Miscellaneous Details
Testing
We use a phonograph turntable to test the SHIMMERs. You should see a
continuous sine wave from at least two of the three channels if the
turntable is tilted.
Power Management
After a successful reset the yellow LED blink. When the Shimmer senses a
lower power condition the red LED begins a double blink pattern: two
short blinks, a long pause and then repeat. During this condition the
accelerometer output sags as seen in the figure below.
In this example the sag is less than 10%. As a precaution the header
block is updated to the SD card with the number of data blocks save up
to this point.
When the power level reaches a critical level the Shimmer attempts to
update the SD card again, and then enters a low power state with no LEDs
lit and no data is saved. The Shimmers battery should be recharged as
quickly as possible.
If the SHIMMERs lithium ion battery is over discharged the factory
warranty is void and the battery may be either hard to charge or its
performance greatly degraded.
SHIMMERs running FTP_DataLogger should be able to collect data for over
one week. Time will vary depending on
* temperature: warmer temperatures should increase battery life
* Age and condition of battery: newer battery that has not been over
discharge should do better.
* Battery charger: charger may not fully charge battery
* Bluetooth module: removal of module will increase battery life
*WARNING*
Sometimes the low voltage detection comes on just when the SHIMMER has
been configured to collected data. If this is the case try again. It
appears to happen after the SHIMMER is charged and the battery is full.
Notes
As of December 18^th , 2007:
Click on the file ignoreFTDISerialNumber.reg to update the Microsoft
Windows registry to have all of your docking station appear the same to
Windows.
* *
--- NEW FILE: crc16.h ---
#ifndef CRC16_H_
#define CRC16_H_
// CCITT CRC16 lookup table
uint16_t CRC_table[] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
};
uint16_t updcrc(uint16_t p_crc, char* cp, int size)
{
uint16_t crc = p_crc;
while (size-- > 0) {
uint16_t partOne = (crc << 8) & 0xFF00;
uint16_t aByte = *cp++ & 0xFF;
crc = partOne ^ CRC_table[(crc>>8) ^ aByte];
}
return (crc);
}
// Calculate CCITT CRC16.
uint16_t CRC16(char* buffer, int byteSize)
{
return updcrc(0x0000, buffer, byteSize);
}
#endif /*CRC16_H_*/
--- NEW FILE: shimmerDisk.c ---
#include <stdlib.h> /* Standard library */
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <time.h>
#include <math.h>
#include <stdint.h>
#include "shimmerSerial.h"
#include "crc16.h"
void getAndPrintAllDataBlocks ();
void printADCdata( FILE* fd,
char* buffer,
time_t* LinuxStartTimeSec,
uint32_t firstBlockShimmerTime,
time_t* LinuxBlockTimeSec,
BOOL* blockOutOfSequenceFlag);
char* getDataDiskBlock( FILE* filePointer, uint blockNumber, char buffer[]);
void getHeaderBlock (FILE* filePointer,
dataCollectionStatus_t* status,
uint32_t* blocksToRead,
time_t* LinuxstartTimeSec);
//#define _DEBUG_SERIAL
//////////////////////////////////////////////////////////////////////////
int main( int argc, char ** argv ) {
fprintf (stderr,
"Shimmer Disk version %s \n\n", SHIMMER_VERSION);
///////////////////////////////////////////////////////////////////
// error checking
HEADER_BLOCK_STRUCTURE headerBlock;
if (sizeof(headerBlock) != BLOCK_LENGTH) {
fprintf (stderr,
"\nERROR: header block structure length: %i, is not equal to block length: %i\n\n",
sizeof(headerBlock), BLOCK_LENGTH);
}
DATA_BLOCK_STRUCTURE dataBlock;
if (sizeof(dataBlock) != BLOCK_LENGTH) {
fprintf (stderr,
"\nERROR: data block structure length: %i, is not equal to block length: %i\n\n",
sizeof(dataBlock), BLOCK_LENGTH);
}
///////////////////////////////////////////////////////////////////
char deviceName[128];
/* get device name */
if (argc == 2) {
strcpy( deviceName, argv[1] );
fprintf (stderr, "Attempting to access %s \n", deviceName);
} else {
fprintf (stderr, "SHIMMER Disk Data Parser\n");
fprintf (stderr, "Usage %s <devname>\n", argv[0] );
fprintf (stderr, "Give the name of the device that has the SD card with the SHIMMER data\n");
fprintf (stderr, "Example device names would be /dev/sdb for the second hard drive\n");
fprintf (stderr, " /dev/sdc for the third hard drive\n");
exit (0);
}
FILE* filePointer = fopen(deviceName, "rb");
if (filePointer == NULL) {
perror ("Unable to open port to disk containing SHIMMER data");
exit(0);
}
getAndPrintAllDataBlocks (filePointer);
fprintf (stderr, "Closing file");
fclose (filePointer);
exit(0);
}
// get all the data blocks from Shimmer
////////////////////////////////////////////////////
void getAndPrintAllDataBlocks (FILE* filePointer) {
DATA_BLOCK_STRUCTURE block;
uint32_t i;
dataCollectionStatus_t status;
uint32_t blocksToRead;
time_t LinuxStartTimeSec;
uint32_t firstBlockShimmerTime;
time_t LinuxBlockTimeSec;
uint32_t experimentID;
BOOL ignoreBlocksToReadFlag;
BOOL timeStampOutOfSequence = FALSE;
#define BLOCK_MESSAGE_FREQUENCY 50
memset(&block, 1, sizeof(block));
getHeaderBlock(filePointer, &status, &blocksToRead, &LinuxStartTimeSec);
// need to subtract the timeStamp from the first block
// from all subsequent blocks
getDataDiskBlock(filePointer, FIRST_DATA_BLOCK_OFFSET, (char*) &block);
firstBlockShimmerTime = block.ShimmerTime;
LinuxBlockTimeSec = LinuxStartTimeSec ;
experimentID = LinuxStartTimeSec & 0xFFFF;
if ( (status == nullStatus) ||
(blocksToRead == 0) ) {
ignoreBlocksToReadFlag = TRUE;
fprintf (stderr,
"\n\nHeader block indicates that the data collection was interrupted. \n" );
fprintf (stderr,
" Retriving data blocks that have a experminent ID of %d . \n\n",
(unsigned int)experimentID );
} else {
ignoreBlocksToReadFlag = FALSE;
}
i = FIRST_DATA_BLOCK_OFFSET;
while ( i < FIRST_DATA_BLOCK_OFFSET + blocksToRead ||
(ignoreBlocksToReadFlag && experimentID == block.experimentID ) ) {
if( (i % BLOCK_MESSAGE_FREQUENCY ) == 0 ) {
fprintf (stderr, "\nRequesting data collected at %s .",
ctime(&LinuxBlockTimeSec));
} else {
fprintf (stderr, ".");
}
if (getDataDiskBlock(filePointer, i, (char*) &block) ) {
if (experimentID == block.experimentID) {
// side effect is to change timeOffset when there
// is a shimmer clock overflow and
// to set timeStampOutOf Sequence flag
printADCdata(stdout, (char*) &block,
&LinuxStartTimeSec, firstBlockShimmerTime,
&LinuxBlockTimeSec,
&timeStampOutOfSequence);
} else if (block.experimentID != 0) {
fprintf (stderr,
"\nBlock ID does not match, Stopping data dump. \n\n");
} else {
fprintf (stderr,
"\nBlock ID of block %i is zero. Continuing data dump. \n\n",
(unsigned int)i);
}
if(timeStampOutOfSequence) {
fprintf (stderr,
"\nTime stamp of block %i is out of sequence. Continuing data dump. \n\n",
(unsigned int)i);
}
i++;
}
}
fprintf (stderr, "\n");
}
// write the ADC data to the file descriptor
// Format of each line
// time x y z
// the separator between fields is given by SEPARATOR
//
// main task is keeping track of time.
// zero the shimmer time - the shimmer time of the first block is really zero
// overflows - shimmer time overflows.
////////////////////////////////////////////////////
void printADCdata( FILE* fd,
char* buffer,
time_t* LinuxStartTimeSec,
uint32_t firstBlockShimmerTime,
time_t* LinuxBlockTimeSec,
BOOL* blockOutOfSequenceFlag) {
char SEPARATOR[] = " ";
int i;
SAMPLE_STRUCTURE* samples;
static double deltaTime = (double)ADC_TIMER_PERIOD_MSEC / TINYOS_TIMER_TICS_SEC;
static double clockInverted = 1.0 / ((double) TINYOS_CLOCK_TICS_SEC);
// number of seconds that can be counted before the TinyOS clock overflows
// 2^32 / 2^15 = 2^17 = 131072
static double shimmerClockOverflowPeriodSec = 131072;
double shimmerTimeSec;
static double oldShimmerTimeSec = 0;
#ifdef _DEBUG_SERIAL
FILE *fdi = fopen("foofoo.exe", "w");
if(fdi > 0) {
fwrite (buffer, 1, BLOCK_LENGTH, fdi);
}
fclose(fdi);
#endif
DATA_BLOCK_STRUCTURE* block = (DATA_BLOCK_STRUCTURE*) buffer;
shimmerTimeSec = (double) ((*block).ShimmerTime - firstBlockShimmerTime) * clockInverted;
// check for overflow
// if overflow we need to add time to clock offset
// this is hard to test because it only happens after 36.4 hours
double shimmerTimeDifferenceBetweenBlocks = shimmerTimeSec - oldShimmerTimeSec;
if (shimmerTimeDifferenceBetweenBlocks < 0) {
*LinuxStartTimeSec += shimmerClockOverflowPeriodSec;
}
oldShimmerTimeSec = shimmerTimeSec;
double blockStartTime = *LinuxStartTimeSec + shimmerTimeSec;
*LinuxBlockTimeSec = (time_t)blockStartTime;
// keep track of start times of blocks to insure that blocks are insequence
// initialze to time of first block
static double lastBlockStartTime = -1.0;
if (lastBlockStartTime == -1.0) {
lastBlockStartTime = blockStartTime;
}
double difference = (blockStartTime - lastBlockStartTime);
if ( difference > 2.0* DURATION_OF_BLOCK_SEC ) {
*blockOutOfSequenceFlag = TRUE;
lastBlockStartTime = blockStartTime;
return;
} else {
lastBlockStartTime = blockStartTime;
*blockOutOfSequenceFlag = FALSE;
}
samples = &((*block).data[0]);
for (i = 0; i < NUMBER_OF_SAMPLES_PER_STRUCTURE; i++) {
fprintf(fd, "%10.4f", blockStartTime + (double)i * deltaTime);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].x);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].y);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].z);
fprintf(fd, "\n");
}
}
// get header block from Shimmer
////////////////////////////////////////////////////
void getHeaderBlock (FILE* filePointer,
dataCollectionStatus_t* status,
uint32_t* blocksToRead,
time_t* LinuxStartTimeSec) {
HEADER_BLOCK_STRUCTURE header;
memset(&header, 1, sizeof(header));
long byteAddress = (SD_START_BLOCK) * BLOCK_LENGTH;
// to speed things up this should probably
// be only done for first block in sequence
rewind(filePointer);
size_t len;
if(fseek(filePointer, byteAddress, SEEK_SET)) {
perror ("Unable to seek to data block");
exit(0);
} else {
len = fread((char*) &header, 1, BLOCK_LENGTH, filePointer);
}
// check if block is real SHIMMER data
uint16_t blockCRC = CRC16( (char*) &header, BLOCK_LENGTH-2);
if ( (header.CRC) != blockCRC ) {
fprintf (stderr, "ERROR: SD block has bad CRC. Block read failed. \n");
fprintf (stderr, "Possible bad SD card or SD card is missing. \n");
exit(0);
}
// blocks written includes the header block
*status = header.dataCollectionStatus;
*blocksToRead = header.numberOfBlocksWritten - 1;
*LinuxStartTimeSec = header.LinuxStartTimeSec;
if (*LinuxStartTimeSec == 0) {
fprintf (stderr, "ERROR: bad header block. Try again.\n");
exit(0);
}
if(strcmp(header.ShimmerVer, SHIMMER_VERSION) ) {
fprintf (stderr, "\n WARNING SOFTWARE VERSIONS DO NOT MATCH.\n");
fprintf (stderr, "\nShimmer firmware: %s, PC software: %s \n",
header.ShimmerVer, SHIMMER_VERSION );
fprintf (stderr, " Data fields are possibly misaligned. Check if data looks reasonable.\n\n");
}
fprintf (stderr, "Shimmer Type: %s \n",
header.ShimmerType);
fprintf (stderr, "Release Date: %s \n",
header.ShimmerDate);
fprintf (stderr, "Shimmer ID: %s \n",
header.ShimmerID );
fprintf (stderr, "Shimmer Status: %s \n",
STATUS_MESSAGES[*status]);
fprintf (stderr, "Starting time of data collection: %s",
ctime(LinuxStartTimeSec));
fprintf (stderr,
"SHIMMER collected %d minutes of data in %d blocks. \n",
(unsigned int)(*blocksToRead * DURATION_OF_BLOCK_SEC / 60),
(unsigned int)*blocksToRead);
}
// get block of data from Shimmer
////////////////////////////////////////////////////
char* getDataDiskBlock( FILE* filePointer, uint blockNumber, char buffer[]) {
long byteAddress = (blockNumber + SD_START_BLOCK)
* BLOCK_LENGTH;
// to speed things up this should probably
// be only done for first block in sequence
rewind(filePointer);
size_t len;
if(fseek(filePointer, byteAddress, SEEK_SET)) {
perror ("Unable to seek to data block");
exit(0);
} else {
len = fread(buffer, 1, BLOCK_LENGTH, filePointer);
}
// verify that the block has not been corrupted.
// This is mostly a check of whether there has been a problem with the SD card.
// Some SD cards do seem to work well with SHIMMER. If the SHIMMER was unable
// to get a block from the SD card it will light the red LED and send a block of
// junk data
DATA_BLOCK_STRUCTURE* block = (DATA_BLOCK_STRUCTURE*)buffer;
uint16_t blockCRC = CRC16( buffer, BLOCK_LENGTH-2);
if ( (block -> CRC) != blockCRC ) {
fprintf (stderr, "ERROR: SD block has bad CRC. Block read failed. \n");
fprintf (stderr, "Possible bad SD card, SD card is missing or reading past end of data. \n");
exit(0);
}
return buffer;
}
--- NEW FILE: shimmerSerial.c ---
#include <stdlib.h> /* Standard library */
#include <stdio.h> /* Standard input/output definitions */
#include <string.h> /* String function definitions */
#include <unistd.h> /* UNIX standard function definitions */
#include <fcntl.h> /* File control definitions */
#include <errno.h> /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <time.h>
#include <math.h>
#include <stdint.h>
//typedef unsigned char uint8_t;
//typedef unsigned short int uint16_t;
//typedef unsigned int uint32_t;
#define BLOCK_MESSAGE_FREQUENCY 50
#include "crc16.h"
#include "shimmerSerial.h"
// FUNCTION PROTOTYPES
int openSerialPort (char defaultPort[]);
void setPortConfiguration (int fd);
BOOL isShimmer (int fd);
void printHelp ();
void parseCommandLineInputs (int argc, char ** argv);
void initTimeStructure (char* arg, uint32_t* timeSec );
void startRecording ();
void toggleRedLed ();
uint32_t getShimmerTime ();
uint32_t getShimmerTimeStartOfDataCollection ();
void setShimmerTimeStartOfDataCollection (const uint32_t time);
uint32_t calculateShimmerTimeOffset ();
void sendHeaderBlock (char* ShimmerID);
void getHeaderBlock (dataCollectionStatus_t* status,
uint32_t* blocksToRead,
uint32_t* LinuxstartTimeSec);
void getAndPrintAllDataBlocks ();
void getAndPrintData (const uint32_t requestTimeSec,
const uint32_t* requestedDurationSec);
void getAndPrintDataBlocks (const uint32_t startBlock,
const uint32_t endBlock,
const uint32_t LinuxTimeFirstBlockSec);
char* getDataBlock (const uint blockNumber, char buffer[]);
void printADCdata(FILE *fd, char* buffer,
uint32_t* LinuxstartTimeSec,
uint32_t firstBlockShimmerTime,
uint32_t* blockTime,
BOOL* blockOutOfSequenceFlag);
void sendUint (int fd, uint blockNumber);
int getSerialBlock (int fd, unsigned char buffer[], int length);
int getSerialChar (int fd, char* aChar);
// GLOBALS
int PORT_DESCRIPTOR = -1; // port to Shimmer
//#define _DEBUG_SERIAL
//////////////////////////////////////////////////////////////////////////
int main( int argc, char ** argv ) {
fprintf (stderr,
"Shimmer Serial version %s \n\n", SHIMMER_VERSION);
///////////////////////////////////////////////////////////////////
// error checking
HEADER_BLOCK_STRUCTURE headerBlock;
if (sizeof(headerBlock) != BLOCK_LENGTH) {
fprintf (stderr,
"\nERROR: header block structure length: %i, is not equal to block length: %i\n\n",
sizeof(headerBlock), BLOCK_LENGTH);
}
DATA_BLOCK_STRUCTURE block;
if (sizeof(block) != BLOCK_LENGTH) {
fprintf (stderr,
"\nERROR: data block structure length: %i, is not equal to block length: %i\n\n",
sizeof(block), BLOCK_LENGTH);
}
///////////////////////////////////////////////////////////////////
// parse command line input to determine what to do
//---------------------------------------------------
parseCommandLineInputs (argc, argv);
fprintf (stderr, "Closing port");
close (PORT_DESCRIPTOR);
exit(0);
}
////////////////////////////////////////////////////////
// search for Shimmer on serial ports
// and then open link. This works on Cygwin
////////////////////////////////////////////////////////
int openSerialPort( char defaultPort[] ){
// if port is open already return
if (PORT_DESCRIPTOR != -1) {
return PORT_DESCRIPTOR;
}
#define PORT_LENGTH 30
static char basePortString[] = "/dev/ttyS%d";
char portString[PORT_LENGTH];
static int FIRST_PORT = 3;
static int NUMBER_OF_PORTS = 10;
int fd;
// search for the serial port connected to the Shimmmer is not as easy
// as it first seems. Problems:
// The Windows device manager does not always report the correct addresses
// of the USB serial ports assigned to the docking station. Windows might
// report port 9 and 10 when in reality it is mapped to port 8 and 9
//
// if the docking station has an even-odd mapping, and the even port is the
// programming port, writing to the programming port will reset the shimmer
// (the LEDs flicker and the green and yellow LEDs stay lit. )
if (defaultPort == NULL) {
int i = -1;
do {
i++;
// look for shimmer on the listed ports
sprintf (portString, basePortString, i + FIRST_PORT);
fprintf (stderr, "Attempting port %s \n", portString);
fd = open (portString, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK );
if ( (fd != -1) && (isShimmer (fd) == FALSE) ) {
fd = -1;
}
} while( (fd < 0) && (i < NUMBER_OF_PORTS) );
}
else {
strncpy (portString, defaultPort, PORT_LENGTH);
fprintf (stderr, "Attempting specified port %s \n", portString);
fd = open (portString, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK );
fprintf (stderr, "fd is %d \n", fd);
if ( (fd != -1) && (isShimmer (fd) == FALSE) ) {
fd = -1;
}
}
// I get segmentation fault if I disconnect Shimmer
// I never seem to get here.
// fprintf (stderr, "fd %i \n", fd);
if (fd < 0) {
perror ("Unable to open port to Shimmer");
exit(0);
}
fprintf (stderr, "Port %s open to Shimmer.\n", portString);
return fd;
}
// check if shimmer by setting time and reading it back
////////////////////////////////////////////////////////
BOOL isShimmer (int fd){
setPortConfiguration (fd);
PORT_DESCRIPTOR = fd;
uint32_t timeOffset = 875634;
setShimmerTimeStartOfDataCollection (timeOffset);
uint32_t testOffset = getShimmerTimeStartOfDataCollection ();
if (testOffset == timeOffset) {
return TRUE;
} else {
close (fd);
return FALSE;
}
}
////////////////////////////////////////////////////////
// BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
// *************************** WARNING *****************************
// This did not work on cywin 10-27-7 had to use cfsetispeed
// see http://www.easysw.com/~mike/serial/serial.html
// *************************** WARNING *****************************
// CRTSCTS : output hardware flow control (only used if the cable has
// all necessary lines. See sect. 7 of Serial-HOWTO)
// CS8 : 8n1 (8bit,no parity,1 stopbit)
// CLOCAL : local connection, no modem contol
// CREAD : enable receiving characters
////////////////////////////////////////////////////////
void setPortConfiguration (int fd) {
struct termios newtio;
bzero(&newtio, sizeof(newtio));
newtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
newtio.c_iflag = 0;
newtio.c_oflag = 0;
// set input mode (non-canonical, no echo,...)
newtio.c_lflag = 0;
cfsetispeed(&newtio, BAUDRATE);
cfsetospeed(&newtio, BAUDRATE);
// does not seem to work in CYQWIN
newtio.c_cc[VTIME] = 2; // inter-character timer in intervals of 0.1 sec ?
newtio.c_cc[VMIN] = 0; // blocking read until x chars received
tcsetattr(fd, TCSANOW, &newtio);
fcntl(fd, F_SETFL, 0);
}
////////////////////////////////////////////////////////
void printHelp () {
fprintf (stderr, "SHIMMER data collection interface\n");
fprintf (stderr, "ss [-p portString] [-AFHfds}\n");
fprintf (stderr, "\n");
fprintf (stderr, "Options for shimmerSerial: \n");
fprintf (stderr, " -p str set serial port\n");
fprintf (stderr, " -A str start data collection on Shimmer\n");
fprintf (stderr, " sets Shimmer ID to str (maximum length %i characters)\n", HEADER_ID_LENGTH);
fprintf (stderr, " initialize clock offset on Shimmer to current UTC \n");
fprintf (stderr, " -F reads all data blocks written to SD card and writes them to stdout\n");
fprintf (stderr, " Example usage: ./ss -F >data.txt \n");
fprintf (stderr, " Press the user button (furthest from USB \n");
fprintf (stderr, " connector) before using this command\n");
fprintf (stderr, " -T MMDDYYYYHHMM EE \n");
fprintf (stderr, " Dump data beginning with time stamp of month: MM, day: DD,\n");
fprintf (stderr, " year: YYYY, hour: HH, minute: MM for EE minutes \n");
fprintf (stderr, " -H display data from header block\n");
fprintf (stderr, " -f x read data block x and write to stdout\n");
fprintf (stderr, " -d toggling red LED (good for testing USB connection)\n");
fprintf (stderr, " -s get Shimmer time \n");
fprintf (stderr, "\n");
fprintf (stderr, "Place SHIMMER in dock station before running ss\n");
}
////////////////////////////////////////////////////////
void parseCommandLineInputs(int argc, char ** argv) {
int i;
uint blockNumber;
char buffer[BLOCK_LENGTH];
if (argc == 1) {
printHelp ();
return;
}
for (i = 1; i < argc; i++ ) {
if (argv[i][0] == '-') {
if (argv[1][1] == 'p' && i == 1) { // only allow as first option
i++;
PORT_DESCRIPTOR = openSerialPort(argv[i]);
}
else if (argv[i][1] == 'A') {
// next argument should be a number
///////////////////////////////////
char* ID_string;
i++;
if ( i < argc) {
ID_string = argv[i];
}
else {
ID_string = "No ID";
}
// start connection to shimmer
PORT_DESCRIPTOR = openSerialPort (NULL);
if(PORT_DESCRIPTOR == -1) {
exit(1);
}
sendHeaderBlock (ID_string);
fprintf (stderr, "Shimmer ID %s \n Starting data collection \n", ID_string);
startRecording ();
fprintf (stderr, " Check if green and orange LEDs are blinking \n");
return;
}
////////////////////////////////////////////////////////////
else if (argv[i][1] == 'F') {
// start connection to shimmer
PORT_DESCRIPTOR = openSerialPort (NULL);
if(PORT_DESCRIPTOR == -1) {
exit(1);
}
getAndPrintAllDataBlocks ();
return;
}
////////////////////////////////////////////////////////////
else if (argv[i][1] == 'T') {
uint32_t requestTimeSec;
uint32_t requestedDurationSec;
i++;
if ( i < argc) {
initTimeStructure (argv[i], &requestTimeSec);
}
else {
requestTimeSec = 0;
}
i++;
if ( i < argc) {
requestedDurationSec = (uint)atoi(argv[i]) * 60;
}
else {
requestedDurationSec = 10 * 60;
}
if (requestTimeSec == 0 || requestedDurationSec == 0) {
fprintf (stderr,
"Switch -T requires start date and time, MMDDYYYYHHMM, (you have %s)and \n", argv[i-1]);
fprintf (stderr,
"a the number of minutes(you have %s) of data to download. \n", argv[i]);
return;
}
// start connection to shimmer
PORT_DESCRIPTOR = openSerialPort (NULL);
if(PORT_DESCRIPTOR == -1) {
exit(1);
}
getAndPrintData(requestTimeSec, &requestedDurationSec);
return;
}
////////////////////////////////////////////////////////////
else if (argv[i][1] == 'H') {
dataCollectionStatus_t status;
uint32_t blocksToRead;
uint32_t timeOffset;
// start connection to shimmer
PORT_DESCRIPTOR = openSerialPort (NULL);
if(PORT_DESCRIPTOR == -1) {
exit(1);
}
getHeaderBlock(&status, &blocksToRead, &timeOffset);
return;
}
////////////////////////////////////////////////////////////
else if (argv[i][1] == 's') {
// start connection to shimmer
PORT_DESCRIPTOR = openSerialPort (NULL);
if(PORT_DESCRIPTOR == -1) {
exit(1);
}
uint32_t shimmerTime = getShimmerTime();
fprintf (stderr, "Shimmer time (sec): %f\n",
(float) shimmerTime /(float)SHIMMER_TICS_PER_SECOND);
fprintf (stderr,
"PC time (sec): %d\n", (unsigned int)time(NULL) );
return;
}
////////////////////////////////////////////////////////////
else if (argv[i][1] == 'f') {
// next argument should be a number
///////////////////////////////////
i++;
if ( i < argc) {
blockNumber = (uint)atoi(argv[i]);
}
else {
blockNumber = -1;
}
if (blockNumber < 0) {
fprintf (stderr,
"Switch -f requires a positive block number not %s\n", argv[i]);
return;
}
// start connection to shimmer
PORT_DESCRIPTOR = openSerialPort (NULL);
if(PORT_DESCRIPTOR == -1) {
exit(1);
}
if (getDataBlock(blockNumber, buffer) ) {
printADCdata(stdout, buffer, 0, 0, 0, 0);
}
return;
}
////////////////////////////////////////////////////////////
else if (argv[i][1] == 'd') {
// start connection to shimmer
PORT_DESCRIPTOR = openSerialPort (NULL);
if(PORT_DESCRIPTOR == -1) {
exit(1);
}
toggleRedLed ();
return;
}
////////////////////////////////////////////////////////////
else {
fprintf (stderr, "Unknown switch %s\n", argv[i]);
printHelp ();
exit (1);
}
}
else {
printHelp ();
exit (1);
}
}
return;
}
// initialize time structure from ASCII string with the format MMDDYYYYHHMM
// where
// MM is month,
// DD is day of the month
// YYYY is the year
// HH is the hour of the day
// MM is the minute of the hour
// and then get time in seconds from the epoch
////////////////////////////////////////////////////
void initTimeStructure (char* arg, uint32_t* timeSec ) {
struct tm timeStructure;
char tempStr[5];
if(strlen(arg) != 12) {
*timeSec = 0;
}
memset(&timeStructure, 0, sizeof(timeStructure));
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, &arg[0], 2);
timeStructure.tm_mon = atoi(tempStr) - 1;
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, &arg[2], 2);
timeStructure.tm_mday = atoi(tempStr);
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, &arg[4], 4);
timeStructure.tm_year = atoi(tempStr) - 1900;
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, &arg[8], 2);
timeStructure.tm_hour = atoi(tempStr);
memset(tempStr, 0, sizeof(tempStr));
strncpy(tempStr, &arg[10], 2);
timeStructure.tm_min = atoi(tempStr);
*timeSec = mktime(&timeStructure);
}
// start recording data
////////////////////////////////////////////////////
void startRecording () {
write (PORT_DESCRIPTOR, START_RECORDING_CMD, 1);
}
// toggle red led
////////////////////////////////////////////////////
void toggleRedLed () {
fprintf (stderr, "Toggling red LED ... \n");
write (PORT_DESCRIPTOR, TOGGLE_RED_LED_CMD, 1);
}
// get time from Shimmer 's clock.
////////////////////////////////////////////////////
uint32_t getShimmerTime () {
uint32_t time;
write (PORT_DESCRIPTOR, GET_SHIMMER_TIME_CMD, 1);
getSerialBlock(PORT_DESCRIPTOR, (unsigned char*)&time, sizeof(time));
return time;
}
// set and get clock offset from Shimmer.
// The clock offset sent to Shimmer using setShimmerTimeStartOfDataCollection is not used to populate
// the header block, it is only used by the getShimmerTimeStartOfDataCollection command
// the pair of functions are used to test the data connection
////////////////////////////////////////////////////
void setShimmerTimeStartOfDataCollection (const uint32_t time) {
write (PORT_DESCRIPTOR, SET_SHIMMER_CLOCK_OFFSET_CMD, 1);
sendUint(PORT_DESCRIPTOR, time);
}
// returns time offset set by setShimmerTimeStartOfDataCollection
uint32_t getShimmerTimeStartOfDataCollection () {
uint32_t time;
write (PORT_DESCRIPTOR, GET_SHIMMER_CLOCK_OFFSET_CMD, 1);
getSerialBlock(PORT_DESCRIPTOR, (unsigned char*)&time, sizeof(time));
return time;
}
// this use to be complicated but now we are just saving the
// the time on the SHIMMMER as the number of seconds from the LINUX Epoch
// side effect is to test if Shimmer is alive
////////////////////////////////////////////////////////////////////
uint32_t calculateShimmerTimeOffset () {
uint32_t timeAtStart = time(NULL);
setShimmerTimeStartOfDataCollection( (uint32_t) timeAtStart );
uint32_t testTimeAtStart = getShimmerTimeStartOfDataCollection ();
// fprintf (stderr, "Shimmmer clock offset: %d\n", testOffset);
if ( (uint32_t) timeAtStart != testTimeAtStart) {
fprintf (stderr,
"\n ERROR: Unable to set time on Shimmer for the start of data collection.\n");
fprintf (stderr,
"Local offset: %u Shimmer's offset: %u.\n", (unsigned int)timeAtStart,
(unsigned int)testTimeAtStart);
exit(0);
}
return timeAtStart;
}
// send header block with all the fields set
////////////////////////////////////////////////////
void sendHeaderBlock (char* ShimmerID) {
HEADER_BLOCK_STRUCTURE header;
memset(&header, ':', sizeof(header) );
// side effect of clearing the Shimmer clock
// that is used to time stamp each data block
// Must save offset in header file.
header.LinuxStartTimeSec= calculateShimmerTimeOffset ();
fprintf (stderr, "Time at start of data collection saved on SD card: %s\n",
ctime((time_t*)&(header.LinuxStartTimeSec)) );
header.numberOfBlocksWritten = 0;
// must do a string copy
strncpy(header.ShimmerID, ShimmerID, HEADER_ID_LENGTH);
header.dataCollectionStatus = nullStatus;
sprintf((char *)&header.unused,"help me I am in the Shimmer.");
write (PORT_DESCRIPTOR, WRITE_HEADER_BLOCK_CMD, 1);
write (PORT_DESCRIPTOR, &header, BLOCK_LENGTH);
}
// use the start and duration time (seconds from the epoch)
// to determine which blocks to get and print
////////////////////////////////////////////////////
void getAndPrintData (const uint32_t requestTimeSec, const uint32_t* durationSec) {
dataCollectionStatus_t status;
uint32_t blocksToRead;
uint32_t dataStartTimeSec;
uint32_t dataDurationSec;
uint32_t requestedDurationSec = *durationSec;
getHeaderBlock(&status, &blocksToRead, &dataStartTimeSec);
dataDurationSec = (unsigned int)
(blocksToRead * DURATION_OF_BLOCK_SEC);
fprintf (stderr,
"\nData from SHIMMER will be requested starting at %s",
ctime((time_t*) &requestTimeSec));
fprintf (stderr,
"for %d minutes.\n", (unsigned int) requestedDurationSec / 60);
// validate that the request time is in the range
// of available data
if ( requestTimeSec < dataStartTimeSec ||
requestTimeSec > dataStartTimeSec + dataDurationSec) {
fprintf (stderr,
"ERROR: requested start time is out of range." );
return;
}
if ( requestTimeSec + requestedDurationSec >
(dataStartTimeSec + dataDurationSec) ) {
requestedDurationSec = (dataStartTimeSec + dataDurationSec) - requestTimeSec;
fprintf (stderr,
"WARNING: requested duration goes past the end of data. Reset to %d minutes.\n",
(unsigned int) requestedDurationSec / 60);
}
// calculate the blocks we would like to download
uint32_t secondsFromStart = requestTimeSec - dataStartTimeSec;
uint32_t startBlock = secondsFromStart / DURATION_OF_BLOCK_SEC;
uint32_t endBlock = startBlock + (requestedDurationSec / DURATION_OF_BLOCK_SEC);
fprintf (stderr,
"Requesting block %d to %d \n",
(unsigned int)startBlock, (unsigned int)endBlock);
getAndPrintDataBlocks (startBlock, endBlock,
dataStartTimeSec + startBlock * DURATION_OF_BLOCK_SEC);
}
// the time stamp associated with each measurement is only as accurate at
// the time stamp given given for the measurement
/////////////////////////////////////////////////////////////
void getAndPrintDataBlocks (const uint32_t startBlock,
const uint32_t endBlock,
const uint32_t LinuxTimeFirstBlockSec) {
DATA_BLOCK_STRUCTURE block;
uint32_t i;
uint32_t firstBlockShimmerTime;
uint32_t LinuxStartTimeSec;
uint32_t LinuxBlockTimeSec;
BOOL blockTimeSequenceBad = FALSE;
// need to subtract the timeStamp from the first block
// from all subsequent blocks
getDataBlock(FIRST_DATA_BLOCK_OFFSET + startBlock, (char*) &block);
firstBlockShimmerTime = block.ShimmerTime;
LinuxBlockTimeSec = LinuxTimeFirstBlockSec;
LinuxStartTimeSec = LinuxTimeFirstBlockSec;
// blocksToRead = 0; //////////////////////////// DEBUGGING
i = FIRST_DATA_BLOCK_OFFSET + startBlock;
while ( i < FIRST_DATA_BLOCK_OFFSET + endBlock ) {
if(( i % BLOCK_MESSAGE_FREQUENCY) == 0.0 ) {
fprintf (stderr, "\nRequesting data collected at %s",
ctime( (time_t*)&LinuxBlockTimeSec));
} else {
fprintf (stderr, ".");
}
if (getDataBlock(i, (char*) &block) ) {
// fprintf (stderr,
// "block number: %i time: %i\n",
// (unsigned int)i, (unsigned int)block.ShimmerTime);
// side effect is to change timeOffset when there
// is a shimmer clock overflow and
// to set timeStampOutOf Sequence flag
printADCdata(stdout, (char*) &block,
&LinuxStartTimeSec, firstBlockShimmerTime,
&LinuxBlockTimeSec,
&blockTimeSequenceBad);
if(blockTimeSequenceBad) {
fprintf (stderr,
"\n Difference of time stamp (%s)", ctime((time_t*)&LinuxBlockTimeSec) );
fprintf (stderr,
"between block %i and the previous block is too large. Stopping data dump. \n\n",
(unsigned int)i);
}
}
i++;
}
fprintf (stderr, "\n");
}
// get all the data blocks from Shimmer
////////////////////////////////////////////////////
void getAndPrintAllDataBlocks () {
DATA_BLOCK_STRUCTURE block;
uint32_t i;
static uint32_t nonMatchingBlockID_Count;
#define MAX_COUNT_NONMATCHING_BLOCK_ID 5
dataCollectionStatus_t status;
uint32_t blocksToRead;
uint32_t LinuxstartTimeSec;
uint32_t firstBlockShimmerTime;
uint32_t LinuxBlockTimeSec;
uint32_t experimentID;
BOOL ignoreBlocksToReadFlag;
BOOL timeStampOutOfSequence = FALSE;
memset(&block, 1, sizeof(block));
getHeaderBlock(&status, &blocksToRead, &LinuxstartTimeSec);
experimentID = LinuxstartTimeSec & 0xFFFF;
// need to subtract the timeStamp from the first block
// from all subsequent blocks
getDataBlock(FIRST_DATA_BLOCK_OFFSET, (char*) &block);
firstBlockShimmerTime = block.ShimmerTime;
LinuxBlockTimeSec = LinuxstartTimeSec ;
if ( (status == nullStatus) ||
(blocksToRead == 0) ) {
ignoreBlocksToReadFlag = TRUE;
fprintf (stderr,
"\n\nHeader block indicates that the data collection was interrupted. \n" );
fprintf (stderr,
" Retrieving data blocks that have a experimental ID of %d . \n\n",
(unsigned int)experimentID );
} else {
ignoreBlocksToReadFlag = FALSE;
}
i = FIRST_DATA_BLOCK_OFFSET;
while ( i < FIRST_DATA_BLOCK_OFFSET + blocksToRead ||
(ignoreBlocksToReadFlag && experimentID == block.experimentID ) ) {
if( (i % BLOCK_MESSAGE_FREQUENCY) == 0 ) {
fprintf (stderr, "\nRequesting data collect at %s .",
ctime((time_t*)&LinuxBlockTimeSec));
} else {
fprintf (stderr, ".");
}
if (getDataBlock(i, (char*) &block) ) {
if (experimentID == block.experimentID) {
// side effect is to change timeOffset when there
// is a shimmer clock overflow and
// to set timeStampOutOf Sequence flag
printADCdata(stdout, (char*) &block,
&LinuxstartTimeSec, firstBlockShimmerTime,
&LinuxBlockTimeSec,
&timeStampOutOfSequence);
nonMatchingBlockID_Count = 0;
} else {
nonMatchingBlockID_Count++;
if(nonMatchingBlockID_Count == MAX_COUNT_NONMATCHING_BLOCK_ID) {
fprintf (stderr,
"\nBlock ID does not match, Stopping data dump. \n\n");
return;
}
fprintf (stderr,
"\nBlock ID does not match, trying next block. \n");
block.experimentID = experimentID;
}
if(timeStampOutOfSequence) {
fprintf (stderr,
"\nTime stamp of block %i is out of sequence. Continuing data dump. \n\n",
(unsigned int)i);
}
}
i++;
}
fprintf (stderr, "\n");
}
// write the ADC data to the file descriptor
// Format of each line
// time x y z
// the separator between fields is given by SEPARATOR
//
// main task is keeping track of time.
// zero the shimmer time - the shimmer time of the first block is really zero
// overflows - shimmer time overflows.
////////////////////////////////////////////////////
void printADCdata( FILE* fd,
char* buffer,
uint32_t* LinuxStartTimeSec,
uint32_t firstBlockShimmerTime,
uint32_t* LinuxBlockTimeSec,
BOOL* blockOutOfSequenceFlag) {
char SEPARATOR[] = " ";
int i;
SAMPLE_STRUCTURE* samples;
static double deltaTime = (double)ADC_TIMER_PERIOD_MSEC / TINYOS_TIMER_TICS_SEC;
static double clockInverted = 1.0 / ((double) TINYOS_CLOCK_TICS_SEC);
// number of seconds that can be counted before the TinyOS clock overflows
// 2^32 / 2^15 = 2^17 = 131072
static double shimmerClockOverflowPeriodSec = 131072;
double shimmerTimeSec;
static double oldShimmerTimeSec = 0;
#ifdef _DEBUG_SERIAL
FILE *fdi = fopen("foofoo.exe", "w");
if(fdi > 0) {
fwrite (buffer, 1, BLOCK_LENGTH, fdi);
}
fclose(fdi);
#endif
DATA_BLOCK_STRUCTURE* block = (DATA_BLOCK_STRUCTURE*) buffer;
shimmerTimeSec = (double) ((*block).ShimmerTime - firstBlockShimmerTime) * clockInverted;
// check for overflow
// if overflow we need to add time to clock offset
// this is hard to test because it only happens after 36.4 hours
double shimmerTimeDifferenceBetweenBlocks = shimmerTimeSec - oldShimmerTimeSec;
if (shimmerTimeDifferenceBetweenBlocks < 0) {
*LinuxStartTimeSec += shimmerClockOverflowPeriodSec;
}
oldShimmerTimeSec = shimmerTimeSec;
double blockStartTime = *LinuxStartTimeSec + shimmerTimeSec;
*LinuxBlockTimeSec = (uint32_t)blockStartTime;
// keep track of start times of blocks to insure that blocks are insequence
// initialze to time of first block
static double lastBlockStartTime = -1.0;
if (lastBlockStartTime == -1.0) {
lastBlockStartTime = blockStartTime;
}
double difference = (blockStartTime - lastBlockStartTime);
if ( difference > 2.0* DURATION_OF_BLOCK_SEC ) {
*blockOutOfSequenceFlag = TRUE;
lastBlockStartTime = blockStartTime;
return;
} else {
lastBlockStartTime = blockStartTime;
*blockOutOfSequenceFlag = FALSE;
}
samples = &((*block).data[0]);
for (i = 0; i < NUMBER_OF_SAMPLES_PER_STRUCTURE; i++) {
fprintf(fd, "%10.4f", blockStartTime + (double)i * deltaTime);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].x);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].y);
fprintf(fd, SEPARATOR);
fprintf(fd, "%5i", samples[i].z);
fprintf(fd, "\n");
}
}
// get header block from Shimmer
////////////////////////////////////////////////////
void getHeaderBlock (dataCollectionStatus_t* status,
uint32_t* blocksToRead,
uint32_t* LinuxstartTimeSec) {
HEADER_BLOCK_STRUCTURE header;
memset(&header, 1, sizeof(header));
getDataBlock (HEADER_BLOCK_OFFSET, (char*) &header);
// blocks written includes the header block
*status = header.dataCollectionStatus;
*blocksToRead = header.numberOfBlocksWritten - 1;
*LinuxstartTimeSec = header.LinuxStartTimeSec;
if (LinuxstartTimeSec == 0) {
fprintf (stderr, "ERROR: bad header block. Try again.\n");
exit(0);
}
if(strcmp(header.ShimmerVer, SHIMMER_VERSION) ) {
fprintf (stderr, "\n WARNING SOFTWARE VERSIONS DO NOT MATCH.\n");
fprintf (stderr, "\nShimmer firmware: %s, PC software: %s \n",
header.ShimmerVer, SHIMMER_VERSION );
fprintf (stderr, " Data fields are possibly misaligned. Check if data looks reasonable.\n\n");
}
fprintf (stderr, "Shimmer Type: %s \n",
header.ShimmerType);
fprintf (stderr, "Release Date: %s \n",
header.ShimmerDate);
fprintf (stderr, "Shimmer ID: %s \n",
header.ShimmerID );
fprintf (stderr, "Shimmer Status: %s \n",
STATUS_MESSAGES[*status]);
fprintf (stderr, "Starting time of data collection: %s",
ctime((time_t*)LinuxstartTimeSec));
fprintf (stderr,
"SHIMMER collected %d minutes of data in %d blocks. \n",
(unsigned int)(*blocksToRead * DURATION_OF_BLOCK_SEC / 60),
(unsigned int)*blocksToRead);
}
// get block of data from Shimmer
////////////////////////////////////////////////////
char* getDataBlock(uint blockNumber, char buffer[]) {
int markerCount = 0;
unsigned char aChar;
const unsigned char headerChar = 0xAA;
// print out occasional message
write (PORT_DESCRIPTOR, READ_BLOCK_CMD , 1);
// Shimmer expects a binary four byte integer for the block number.
// little endian - low byte first
//-----------------------------------------------------
sendUint (PORT_DESCRIPTOR, blockNumber);
// block is preceded with four bytes of 0xAA
int maxRetryCount = 30;
while (markerCount < 4 && maxRetryCount > 0) {
if (getSerialChar(PORT_DESCRIPTOR, &aChar)) {
if (aChar == headerChar) {
markerCount++;
if (markerCount == 4) {
break;
}
} else {
markerCount = 0;
}
maxRetryCount--;
} else {
maxRetryCount--;
}
}
if (markerCount != 4) {
fprintf (stderr,
"\n BAD MESSAGE HEADER. Block %i read failed. \n",
blockNumber - FIRST_DATA_BLOCK_OFFSET);
exit (0);
}
getSerialBlock(PORT_DESCRIPTOR, buffer, BLOCK_LENGTH);
// verify that the block has not been corrupted.
// This is mostly a check of whether there has been a problem with the SD card.
// Some SD cards do seem to work well with SHIMMER. If the SHIMMER was unable
// to get a block from the SD card it will light the red LED and send a block of
// junk data
//
// a SHIMMER without an SD card can send back a block of zeros which
// is not good because the CRC matches
int i, sum = 0;
for (i = 0; i < BLOCK_LENGTH; i++) {
sum += buffer [i];
}
if ( sum == 0 ) {
fprintf (stderr,
"ERROR: SD block %i was all zeros. Block read failed. \n",
blockNumber - FIRST_DATA_BLOCK_OFFSET);
fprintf (stderr, "Possible bad SD card or SD card is missing. \n");
exit(0);
}
DATA_BLOCK_STRUCTURE* block = (DATA_BLOCK_STRUCTURE*)buffer;
uint16_t blockCRC = CRC16( buffer, BLOCK_LENGTH-2);
if ( (block -> CRC) != blockCRC ) {
fprintf (stderr,
"\nERROR: SD block %i has bad CRC. Block read failed. \n",
blockNumber - FIRST_DATA_BLOCK_OFFSET);
#ifdef _DEBUG_SERIAL
// debugging code to dump block
fprintf (stderr, " \n Dumping block received...\n");
int i;
for (i = 0; i < BLOCK_LENGTH; i++) {
fprintf (stderr, "%2x ", (unsigned char)(buffer [i]));
if( ((i + 1) % 16) == 0) {
fprintf (stderr, " \n");
}
}
fprintf (stderr, " \n");
#endif
}
#ifdef _DEBUG_SERIAL
// debugging code to dump block
fprintf (stderr, " \n Dumping block received...\n");
int i;
for (i = 0; i < BLOCK_LENGTH; i++) {
fprintf (stderr, "%2x ", (unsigned char)(buffer [i]));
if(((double)(i + 1) % 16.0) == 0.0) {
fprintf (stderr, " \n");
}
}
fprintf (stderr, " \n");
#endif
return buffer;
}
// write a four byte integer to the serial port one byte at a time
////////////////////////////////////////////////////////////////////
void sendUint (int fd, uint blockNumber) {
int i;
char c;
for (i = 0; i < 4; i++) {
c = (char) (blockNumber >> (i *8 ) );
c = (char) (blockNumber >> (i *8 ) ) & 0xFF;
write (fd, &c, 1);
// fprintf (stderr, "%u \n", (unsigned char)c);
}
}
// wait until character is received from port
////////////////////////////////////////////////////
int getSerialChar (int fd, char* aChar){
int numberOfCharacters = -1;
// fprintf (stderr, "getting char from shimmer ");
#define MAX_RETRY_COUNT_READ 10
int i = MAX_RETRY_COUNT_READ;
while( (i > 0) && (numberOfCharacters <= 0) ) {
numberOfCharacters = read(fd, aChar, 1);
i--;
}
if (numberOfCharacters == 0){
fprintf (stderr, "ERROR: Read from serial port timed out. \n" );
// exit(-1);
}
return numberOfCharacters;
}
// wait until length characters are received from port
////////////////////////////////////////////////////
int getSerialBlock (int fd, unsigned char buffer[], int length){
int charCount = 0;
while( charCount < length ) {
if (getSerialChar(fd, &(buffer [charCount]) ) == 0 )
return 0;
charCount++;
}
#ifdef _DEBUG_SERIAL
// debugging code to dump block
fprintf (stderr, " \n Dumping block received...\n");
int i;
for (i = 0; i < length; i++) {
fprintf (stderr, "%2x ", (unsigned char)(buffer [i]));
if(((double)(i + 1) % 16.0) == 0.0) {
fprintf (stderr, " \n");
}
}
fprintf (stderr, " \n");
#endif
return charCount;
}
--- NEW FILE: shimmerSerial.h ---
#ifndef __shimmerSerial_h
#define __shimmerSerial_h
#define SHIMMER_VERSION "1.20"
#define SHIMMER_DATE "25-Feb-08"
typedef enum {
Accel_Shimmer = 0,
BT_Shimmer
} Shimmer_t;
char* SHIMMER_TYPE_STRINGS[] = {
"3-axis accel",
"BT data aggregator" };
#define SHIMER_TYPE SHIMMER_TYPE_STRINGS[Accel_Shimmer]
char* SHIMMER_ACCEL_GAIN_STRINGS[] = {
"1.5g",
"2.0g",
"4.0g",
"6.0g" };
// not saved on SD card yet
#define SHIMER_ACCELEROMETER_GAIN RANGE_6_0G
// the settings to generate the correct baud rate are found in
// contrib\handhelds\tos\platform\msp430_crl_base\clock_settings
// if TARGET_DCO_KHZ is not set than it is defined there.
//
// this value is used to calculate SMCLK_KHZ which is used in
// contrib\handhelds\tos\platform\msp430_crl_base\msp430baudrates.h
// to calculate the baudrate enumeration for UBR_SMCLK_115200, UMCTL_SMCLK_115200
// which is used in initSPI
//#define TARGET_DCO_KHZ 4096 * 2
#define SHIMMER_TICS_PER_SECOND 0x8000
// B256000 does not seem to be supported. It set the baudrate to B115200
//#define BAUDRATE B115200
#define BAUDRATE B230400
#define BLOCK_LENGTH 512
#define HEADER_BLOCK_OFFSET 0
#define FIRST_DATA_BLOCK_OFFSET 1
#define SD_START_BLOCK 2000
#define START_RECORDING_CMD "a"
#define READ_BLOCK_CMD "f"
#define WRITE_HEADER_BLOCK_CMD "m"
#define TOGGLE_RED_LED_CMD "d"
#define GET_SHIMMER_TIME_CMD "s"
#define GET_SHIMMER_CLOCK_OFFSET_CMD "o"
#define SET_SHIMMER_CLOCK_OFFSET_CMD "r"
// Shimmer/TinyOS has two clock/timers that are used.
// The time that is used to schedule the DC conversion
// call sampleTimer.start(TIMER_REPEAT, ADC_TIMER_PERIOD_MSEC);
// has one tic rate TINYOS_TIMER_TICS_SEC
//
// The time stamp that comes with each block is gotten by
// atomic dmaBufferTimeStamp = call LocalTime.read()
// uses a time base with another number of clock ticks per second
//
// if shimmer has a clock with 0x8000 (32k) ticks per second
// and time is saved in 32 bits then it will rollover every
// 36.4 hours
#define ADC_TIMER_PERIOD_MSEC 20
#define TINYOS_TIMER_TICS_SEC 0x0400
#define TINYOS_CLOCK_TICS_SEC 0x8000
#define BACKUP_HEADER_COUNT 40.0
#define LED_BLINK_ON_MSEC 5
typedef char BOOL;
enum {
FALSE = 0,
TRUE = 1 };
typedef enum {
nullStatus = 0, // starting status
completedNormal,
lowPowerWarning,
poweredDown,
periodicSave,
errorWritingBlock // not used yet
} dataCollectionStatus_t;
char* STATUS_MESSAGES[] = {
"Data collection terminated without setting status.",
"Data collection completed normally",
"Low battery warning received.",
"Shimmer powered down because of low battery voltage",
"Shimmer terminated abnormally, loss of some data, \nusing periodically backed up header info",
"Error writing to SD card, possible end of card reached." };
#define UNUSED_BYTES_IN_HEADER 60
#define HEADER_ID_LENGTH 400
#define HEADER_VERSION_LENGTH 10
#define HEADER_TYPE_LENGTH 20
#define HEADER_DATE_LENGTH 10
typedef struct{
uint32_t LinuxStartTimeSec;
uint32_t numberOfBlocksWritten; // includes the first status block
char ShimmerVer [HEADER_VERSION_LENGTH];
char ShimmerType [HEADER_TYPE_LENGTH];
char ShimmerDate [HEADER_DATE_LENGTH];
uint8_t dataCollectionStatus;
uint8_t errorCount; // not used yet
char ShimmerID [HEADER_ID_LENGTH];
uint8_t unused[UNUSED_BYTES_IN_HEADER]; // needed to make block 512 bytes long
uint16_t CRC;
} __attribute__((packed)) HEADER_BLOCK_STRUCTURE;
typedef struct {
uint16_t x;
uint16_t y;
uint16_t z;
} __attribute__ ((packed)) SAMPLE_STRUCTURE;
#define NUMBER_OF_SAMPLES_PER_STRUCTURE 84
typedef struct {
uint32_t ShimmerTime;
SAMPLE_STRUCTURE data[NUMBER_OF_SAMPLES_PER_STRUCTURE];
uint16_t experimentID;
uint16_t CRC;
} __attribute__ ((packed)) DATA_BLOCK_STRUCTURE;
// used by the data conversion and DMA transfer to RAM
//************* needs to be cleaned up *******************
//****** can get out of synch DATA_BLOCK_STRUCTURE ************
//*****************************************************************
#define NUMBER_OF_ADC_CHANNELS 3 // Number of ADC channels used
#define DURATION_OF_BLOCK_SEC ((double) (NUMBER_OF_SAMPLES_PER_STRUCTURE * ADC_TIMER_PERIOD_MSEC) / \
(double)TINYOS_TIMER_TICS_SEC )
#endif
More information about the Tinyos-contrib-commits
mailing list