http://anrg.usc.edu/contiki/api.php?action=feedcontributions&user=Nikhil&feedformat=atomContiki - User contributions [en]2024-03-29T12:15:13ZUser contributionsMediaWiki 1.26.2http://anrg.usc.edu/contiki/index.php?title=Contiki_tutorials&diff=396Contiki tutorials2014-04-12T01:06:44Z<p>Nikhil: </p>
<hr />
<div>[[Main_Page | Back to Main Page]]<br />
<br />
== List of Tutorials ==<br />
<br />
Completed<br />
<br />
# [[Installation]]<br />
# [[Hello World]]<br />
# [[Broadcast Example]]<br />
# [[Collect View]]<br />
# [[Contiki build system]]<br />
<br />
Need review<br />
<br />
# [[Interfacing with Python]]<br />
# [[Timers]] Tim, Leo<br />
<br />
Starting<br />
# [[Tutornet]] Pedro, Kwame<br />
# [[Cooja Simulator]] (Getting started, debugging) Pedro<br />
# [[Protocols stack]]<br />
# [[CFS-Coffee]] Kevin<br />
# [[RSS measurement]]<br />
# [[Sensor acquisition]] (light, temperature) Kwame<br />
<br />
<!--[[Processes]] Yash --><br />
<br />
<pre style="color: red">Be sure to include references in your tutorials, especially if you quote material from other sites!</pre></div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=274CFS-Coffee2014-03-01T00:32:24Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
<code><nowiki>int cfs_open(const char* name, int flags)</nowiki></code><br />
<br />
Open a file with specific flags. <br />
<br />
'''name''': The name of the file want to be opened.<br />
<br />
'''flags''': The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
<br />
'''return value''': A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
<br />
'''e.g''': fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
<br />
<code><nowiki>void cfs_close(int fd)</nowiki></code><br />
<br />
Close a open file.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''e.g''': cfs_close(fd)<br />
<br />
Close the file with file descriptor fd.<br />
<br />
<br />
<code><nowiki>int cfs_read(int fd, void* buf, unsigned int len)</nowiki></code><br />
<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer in which data should be read from the file.<br />
<br />
'''len''': The number of bytes that should be read.<br />
<br />
'''Return Value''': The number of bytes that was actually read from the file or -1 if an error happens<br />
<br />
'''e.g''': ret = cfs_read(fd, buf, sizeof(buf))<br />
<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
<br />
<code><nowiki>int cfs_write(int fd, const void *buf, unsigned int len)</nowiki></code><br />
<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer from which data should be written to the file.<br />
<br />
'''len''': The number of bytes that should be written.<br />
<br />
'''Return Value''': The number of bytes that was actually written to the file.<br />
<br />
'''e.g''': ret = cfs_write(fd, buf, sizeof(buf))<br />
<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was actually written to the file.<br />
<br />
<br />
<code><nowiki>cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)</nowiki></code><br />
<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''offset''': A position, either relative or absolute, in the file.<br />
<br />
'''whence''': Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
<br />
'''Return Value''': The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
<br />
'''e.g''': ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
<br />
Move the file position to the beginning of the file.<br />
<br />
<br />
<code><nowiki>int cfs_remove(const char *name)</nowiki></code><br />
<br />
Remove a file with name name.<br />
<br />
'''name''': The name of the file.<br />
<br />
'''Return Value''': 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
<br />
'''e.g''': ret = sfc_remove(name)<br />
<br />
Remove the file with name pointed by name.<br />
<br />
<br />
<code><nowiki>int cfs_opendir(struct cfs_dir *dirp, const char *name)</nowiki></code><br />
<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that is filled in by the function.<br />
<br />
'''name''': The name of the directory.<br />
<br />
'''Return Value''': 0 or -1 if the directory could not be opened.<br />
<br />
'''e.g''': ret = cfs_opendir(&dir, "/")<br />
<br />
Open the root directory.<br />
<br />
<br />
<code><nowiki>int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)</nowiki></code><br />
<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
'''dirent''': A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
<br />
'''Return Value''': 0 or -1 if no more directory entries can be read.<br />
<br />
'''e.g''': if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
<br />
<code><nowiki>void cfs_closedir(struct cfs_dir *dirp)</nowiki></code><br />
<br />
Close the dirctory dirp opened with cfs_opendir().<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
<br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
<code><nowiki>int cfs_coffee_format(void)</nowiki></code><br />
<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
<br />
<code><nowiki>int cfs_coffee_reserve(const char *name, cfs_offset_t size)</nowiki></code><br />
<br />
Reserve space for a file of name with size.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
<br />
<code><nowiki>int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size)</nowiki></code><br />
<br />
Configure the on-demand log file.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
<br />
<code><nowiki><br />
<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
</nowiki></code><br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
<code><nowiki><br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
</nowiki></code><br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=273CFS-Coffee2014-03-01T00:30:40Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
<code><nowiki>int cfs_open(const char* name, int flags)</nowiki></code><br />
<br />
Open a file with specific flags. <br />
<br />
'''name''': The name of the file want to be opened.<br />
<br />
'''flags''': The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
<br />
'''return value''': A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
<br />
'''e.g''': fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
<br />
<code><nowiki>void cfs_close(int fd)</code></nowiki><br />
<br />
Close a open file.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''e.g''': cfs_close(fd)<br />
<br />
Close the file with file descriptor fd.<br />
<br />
<br />
<code><nowiki>int cfs_read(int fd, void* buf, unsigned int len)</code></nowiki><br />
<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer in which data should be read from the file.<br />
<br />
'''len''': The number of bytes that should be read.<br />
<br />
'''Return Value''': The number of bytes that was actually read from the file or -1 if an error happens<br />
<br />
'''e.g''': ret = cfs_read(fd, buf, sizeof(buf))<br />
<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
<br />
<code><nowiki>int cfs_write(int fd, const void *buf, unsigned int len)</code></nowiki><br />
<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer from which data should be written to the file.<br />
<br />
'''len''': The number of bytes that should be written.<br />
<br />
'''Return Value''': The number of bytes that was actually written to the file.<br />
<br />
'''e.g''': ret = cfs_write(fd, buf, sizeof(buf))<br />
<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was actually written to the file.<br />
<br />
<br />
<code><nowiki>cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)</code></nowiki><br />
<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''offset''': A position, either relative or absolute, in the file.<br />
<br />
'''whence''': Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
<br />
'''Return Value''': The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
<br />
'''e.g''': ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
<br />
Move the file position to the beginning of the file.<br />
<br />
<br />
<code><nowiki>int cfs_remove(const char *name)</code></nowiki><br />
<br />
Remove a file with name name.<br />
<br />
'''name''': The name of the file.<br />
<br />
'''Return Value''': 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
<br />
'''e.g''': ret = sfc_remove(name)<br />
<br />
Remove the file with name pointed by name.<br />
<br />
<br />
<code><nowiki>int cfs_opendir(struct cfs_dir *dirp, const char *name)</code></nowiki><br />
<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that is filled in by the function.<br />
<br />
'''name''': The name of the directory.<br />
<br />
'''Return Value''': 0 or -1 if the directory could not be opened.<br />
<br />
'''e.g''': ret = cfs_opendir(&dir, "/")<br />
<br />
Open the root directory.<br />
<br />
<br />
<code><nowiki>int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)</code></nowiki><br />
<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
'''dirent''': A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
<br />
'''Return Value''': 0 or -1 if no more directory entries can be read.<br />
<br />
'''e.g''': if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
<br />
<code><nowiki>void cfs_closedir(struct cfs_dir *dirp)</code></nowiki><br />
<br />
Close the dirctory dirp opened with cfs_opendir().<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
<br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
<code><nowiki>int cfs_coffee_format(void)</code></nowiki><br />
<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
<br />
<code><nowiki>int cfs_coffee_reserve(const char *name, cfs_offset_t size)</code></nowiki><br />
<br />
Reserve space for a file of name with size.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
<br />
<code><nowiki>int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size) <nowiki><code><br />
<br />
Configure the on-demand log file.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
<br />
<code><nowiki><br />
<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
</nowiki></code><br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
<code><nowiki><br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
</nowiki></code><br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=272CFS-Coffee2014-03-01T00:27:52Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
<code><nowiki>int cfs_open(const char* name, int flags)</nowiki></code><br />
<br />
Open a file with specific flags. <br />
<br />
'''name''': The name of the file want to be opened.<br />
<br />
'''flags''': The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
<br />
'''return value''': A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
<br />
'''e.g''': fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
<nowiki><code>void cfs_close(int fd)</code></nowiki><br />
<br />
Close a open file.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''e.g''': cfs_close(fd)<br />
<br />
Close the file with file descriptor fd.<br />
<br />
<br />
<nowiki><code>int cfs_read(int fd, void* buf, unsigned int len)</code></nowiki><br />
<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer in which data should be read from the file.<br />
<br />
'''len''': The number of bytes that should be read.<br />
<br />
'''Return Value''': The number of bytes that was actually read from the file or -1 if an error happens<br />
<br />
'''e.g''': ret = cfs_read(fd, buf, sizeof(buf))<br />
<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
<br />
<nowiki><code>int cfs_write(int fd, const void *buf, unsigned int len)</code></nowiki><br />
<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer from which data should be written to the file.<br />
<br />
'''len''': The number of bytes that should be written.<br />
<br />
'''Return Value''': The number of bytes that was actually written to the file.<br />
<br />
'''e.g''': ret = cfs_write(fd, buf, sizeof(buf))<br />
<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was actually written to the file.<br />
<br />
<br />
<nowiki><code>cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)</code></nowiki><br />
<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''offset''': A position, either relative or absolute, in the file.<br />
<br />
'''whence''': Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
<br />
'''Return Value''': The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
<br />
'''e.g''': ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
<br />
Move the file position to the beginning of the file.<br />
<br />
<br />
<nowiki><code>int cfs_remove(const char *name)</code></nowiki><br />
<br />
Remove a file with name name.<br />
<br />
'''name''': The name of the file.<br />
<br />
'''Return Value''': 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
<br />
'''e.g''': ret = sfc_remove(name)<br />
<br />
Remove the file with name pointed by name.<br />
<br />
<br />
<nowiki><code>int cfs_opendir(struct cfs_dir *dirp, const char *name)</code></nowiki><br />
<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that is filled in by the function.<br />
<br />
'''name''': The name of the directory.<br />
<br />
'''Return Value''': 0 or -1 if the directory could not be opened.<br />
<br />
'''e.g''': ret = cfs_opendir(&dir, "/")<br />
<br />
Open the root directory.<br />
<br />
<br />
<nowiki><code>int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)</code></nowiki><br />
<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
'''dirent''': A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
<br />
'''Return Value''': 0 or -1 if no more directory entries can be read.<br />
<br />
'''e.g''': if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
<br />
<nowiki><code>void cfs_closedir(struct cfs_dir *dirp)</code></nowiki><br />
<br />
Close the dirctory dirp opened with cfs_opendir().<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
<br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
<nowiki><code>int cfs_coffee_format(void)</code></nowiki><br />
<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
<br />
<nowiki><code>int cfs_coffee_reserve(const char *name, cfs_offset_t size)</code></nowiki><br />
<br />
Reserve space for a file of name with size.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
<br />
<nowiki><code>int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size) <nowiki><code><br />
<br />
Configure the on-demand log file.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=271CFS-Coffee2014-03-01T00:26:34Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
<nowiki><code>int cfs_open(const char* name, int flags)</code></nowiki><br />
<br />
Open a file with specific flags. <br />
<br />
'''name''': The name of the file want to be opened.<br />
<br />
'''flags''': The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
<br />
'''return value''': A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
<br />
'''e.g''': fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
<nowiki><code>void cfs_close(int fd)</code></nowiki><br />
<br />
Close a open file.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''e.g''': cfs_close(fd)<br />
<br />
Close the file with file descriptor fd.<br />
<br />
<br />
<nowiki><code>int cfs_read(int fd, void* buf, unsigned int len)</code></nowiki><br />
<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer in which data should be read from the file.<br />
<br />
'''len''': The number of bytes that should be read.<br />
<br />
'''Return Value''': The number of bytes that was actually read from the file or -1 if an error happens<br />
<br />
'''e.g''': ret = cfs_read(fd, buf, sizeof(buf))<br />
<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
<br />
<nowiki><code>int cfs_write(int fd, const void *buf, unsigned int len)</code></nowiki><br />
<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer from which data should be written to the file.<br />
<br />
'''len''': The number of bytes that should be written.<br />
<br />
'''Return Value''': The number of bytes that was actually written to the file.<br />
<br />
'''e.g''': ret = cfs_write(fd, buf, sizeof(buf))<br />
<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was actually written to the file.<br />
<br />
<br />
<nowiki><code>cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)</code></nowiki><br />
<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''offset''': A position, either relative or absolute, in the file.<br />
<br />
'''whence''': Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
<br />
'''Return Value''': The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
<br />
'''e.g''': ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
<br />
Move the file position to the beginning of the file.<br />
<br />
<br />
<nowiki><code>int cfs_remove(const char *name)</code></nowiki><br />
<br />
Remove a file with name name.<br />
<br />
'''name''': The name of the file.<br />
<br />
'''Return Value''': 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
<br />
'''e.g''': ret = sfc_remove(name)<br />
<br />
Remove the file with name pointed by name.<br />
<br />
<br />
<nowiki><code>int cfs_opendir(struct cfs_dir *dirp, const char *name)</code></nowiki><br />
<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that is filled in by the function.<br />
<br />
'''name''': The name of the directory.<br />
<br />
'''Return Value''': 0 or -1 if the directory could not be opened.<br />
<br />
'''e.g''': ret = cfs_opendir(&dir, "/")<br />
<br />
Open the root directory.<br />
<br />
<br />
<nowiki><code>int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)</code></nowiki><br />
<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
'''dirent''': A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
<br />
'''Return Value''': 0 or -1 if no more directory entries can be read.<br />
<br />
'''e.g''': if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
<br />
<nowiki><code>void cfs_closedir(struct cfs_dir *dirp)</code></nowiki><br />
<br />
Close the dirctory dirp opened with cfs_opendir().<br />
<br />
'''dirp''': A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
<br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
<nowiki><code>int cfs_coffee_format(void)</code></nowiki><br />
<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
<br />
<nowiki><code>int cfs_coffee_reserve(const char *name, cfs_offset_t size)</code></nowiki><br />
<br />
Reserve space for a file of name with size.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
<br />
<nowiki><code>int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size) <nowiki><code><br />
<br />
Configure the on-demand log file.<br />
<br />
'''Return Value''': 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=254CFS-Coffee2014-02-28T04:16:56Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
<nowiki><code>int cfs_open(const char* name, int flags)</code></nowiki><br />
<br />
Open a file with specific flags. <br />
<br />
'''name''': The name of the file want to be opened.<br />
<br />
'''flags''': The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
<br />
'''return value''': A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
<br />
'''e.g''': fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
<nowiki><code>void cfs_close(int fd)</code></nowiki><br />
<br />
Close a open file.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''e.g''': cfs_close(fd)<br />
<br />
Close the file with file descriptor fd.<br />
<br />
<br />
<nowiki><code>int cfs_read(int fd, void* buf, unsigned int len)</code></nowiki><br />
<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer in which data should be read from the file.<br />
<br />
'''len''': The number of bytes that should be read.<br />
<br />
'''Return Value''': The number of bytes that was actually read from the file or -1 if an error happens<br />
<br />
'''e.g''': ret = cfs_read(fd, buf, sizeof(buf))<br />
<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
<br />
<nowiki><code>int cfs_write(int fd, const void *buf, unsigned int len)</code></nowiki><br />
<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer from which data should be written to the file.<br />
<br />
'''len''': The number of bytes that should be written.<br />
<br />
'''Return Value''': The number of bytes that was actually written to the file.<br />
<br />
'''e.g''': ret = cfs_write(fd, buf, sizeof(buf))<br />
<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was actually written to the file.<br />
<br />
<br />
<nowiki><code>cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)</code></nowiki><br />
<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''offset''': A position, either relative or absolute, in the file.<br />
<br />
'''whence''': Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
<br />
'''Return Value''': The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
<br />
'''e.g''': ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
<br />
Move the file position to the beginning of the file.<br />
<br />
<br />
<nowiki><code>int cfs_remove(const char *name)</code></nowiki><br />
<br />
Remove a file with name name.<br />
<br />
'''name''': The name of the file.<br />
<br />
'''Return Value''': 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
<br />
'''e.g''': ret = sfc_remove(name)<br />
<br />
Remove the file with name pointed by name.<br />
<br />
<br />
<nowiki><code>int cfs_opendir(struct cfs_dir *dirp, const char *name)</code></nowiki><br />
<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
dirp: A pointer to a struct cfs_dir that is filled in by the function.<br />
name: The name of the directory.<br />
Return Value: 0 or -1 if the directory could not be opened.<br />
e.g: ret = cfs_opendir(&dir, "/")<br />
Open the root directory.<br />
<br />
int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
dirent: A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
Return Value: 0 or -1 if no more directory entries can be read.<br />
e.g: if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
void cfs_closedir(struct cfs_dir *dirp)<br />
Close the dirctory dirp opened with cfs_opendir().<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
int cfs_coffee_format(void)<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_reserve(const char *name, cfs_offset_t size)<br />
Reserve space for a file of name with size.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size)<br />
Configure the on-demand log file.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=253CFS-Coffee2014-02-28T04:14:46Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
<nowiki><code>int cfs_open(const char* name, int flags)</code></nowiki><br />
<br />
Open a file with specific flags. <br />
<br />
'''name''': The name of the file want to be opened.<br />
<br />
'''flags''': The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
<br />
'''return value''': A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
<br />
'''e.g''': fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
void cfs_close(int fd)<br />
<br />
Close a open file.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''e.g''': cfs_close(fd)<br />
<br />
Close the file with file descriptor fd.<br />
<br />
<br />
int cfs_read(int fd, void* buf, unsigned int len)<br />
<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer in which data should be read from the file.<br />
<br />
'''len''': The number of bytes that should be read.<br />
<br />
'''Return Value''': The number of bytes that was actually read from the file or -1 if an error happens<br />
<br />
'''e.g''': ret = cfs_read(fd, buf, sizeof(buf))<br />
<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
<br />
int cfs_write(int fd, const void *buf, unsigned int len)<br />
<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''buf''': The buffer from which data should be written to the file.<br />
<br />
'''len''': The number of bytes that should be written.<br />
<br />
'''Return Value''': The number of bytes that was actually written to the file.<br />
<br />
'''e.g''': ret = cfs_write(fd, buf, sizeof(buf))<br />
<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was actually written to the file.<br />
<br />
<br />
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)<br />
<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
<br />
'''fd''': The file descriptor of the open file.<br />
<br />
'''offset''': A position, either relative or absolute, in the file.<br />
<br />
'''whence''': Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
<br />
'''Return Value''': The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
<br />
'''e.g''': ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
<br />
Move the file position to the beginning of the file.<br />
<br />
<br />
int cfs_remove(const char *name)<br />
<br />
Remove a file with name name.<br />
<br />
'''name''': The name of the file.<br />
<br />
'''Return Value''': 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
<br />
'''e.g''': ret = sfc_remove(name)<br />
<br />
Remove the file with name pointed by name.<br />
<br />
int cfs_opendir(struct cfs_dir *dirp, const char *name)<br />
<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
dirp: A pointer to a struct cfs_dir that is filled in by the function.<br />
name: The name of the directory.<br />
Return Value: 0 or -1 if the directory could not be opened.<br />
e.g: ret = cfs_opendir(&dir, "/")<br />
Open the root directory.<br />
<br />
int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
dirent: A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
Return Value: 0 or -1 if no more directory entries can be read.<br />
e.g: if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
void cfs_closedir(struct cfs_dir *dirp)<br />
Close the dirctory dirp opened with cfs_opendir().<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
int cfs_coffee_format(void)<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_reserve(const char *name, cfs_offset_t size)<br />
Reserve space for a file of name with size.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size)<br />
Configure the on-demand log file.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=252CFS-Coffee2014-02-28T03:47:58Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
<nowiki><code>int cfs_open(const char* name, int flags)</code></nowiki><br />
Open a file with specific flags. <br />
name: The name of the file want to be opened.<br />
flags: The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
return value: A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
e.g: fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
void cfs_close(int fd)<br />
Close a open file.<br />
fd: The file descriptor of the open file.<br />
e.g: cfs_close(fd)<br />
Close the file with file descriptor fd.<br />
<br />
int cfs_read(int fd, void* buf, unsigned int len)<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
fd: The file descriptor of the open file.<br />
buf: The buffer in which data should be read from the file.<br />
len: The number of bytes that should be read.<br />
Return Value: The number of bytes that was actually read from the file or -1 if an error happens<br />
e.g: ret = cfs_read(fd, buf, sizeof(buf))<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
int cfs_write(int fd, const void *buf, unsigned int len)<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
fd: The file descriptor of the open file.<br />
buf: The buffer from which data should be written to the file.<br />
len: The number of bytes that should be written.<br />
Return Value: The number of bytes that was actually written to the file.<br />
e.g: ret = cfs_write(fd, buf, sizeof(buf))<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was acutually written to the file.<br />
<br />
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
fd: The file descriptor of the open file.<br />
offset: A position, either relative or absolute, in the file.<br />
whence: Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
Return Value: The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
e.g: ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
Move the file position to the beginning of the file.<br />
<br />
int cfs_remove(const char *name)<br />
Remove a file with name name.<br />
name: The name of the file.<br />
Return Value: 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
e.g: ret = sfc_remove(name)<br />
Remove the file with name pointed by name.<br />
<br />
int cfs_opendir(struct cfs_dir *dirp, const char *name)<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
dirp: A pointer to a struct cfs_dir that is filled in by the function.<br />
name: The name of the directory.<br />
Return Value: 0 or -1 if the directory could not be opened.<br />
e.g: ret = cfs_opendir(&dir, "/")<br />
Open the root directory.<br />
<br />
int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
dirent: A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
Return Value: 0 or -1 if no more directory entries can be read.<br />
e.g: if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
void cfs_closedir(struct cfs_dir *dirp)<br />
Close the dirctory dirp opened with cfs_opendir().<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
int cfs_coffee_format(void)<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_reserve(const char *name, cfs_offset_t size)<br />
Reserve space for a file of name with size.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size)<br />
Configure the on-demand log file.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=251CFS-Coffee2014-02-28T03:47:21Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
==Introduction==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
<nowiki><code>int cfs_open(const char* name, int flags)</code></nowiki><br />
Open a file with specific flags. <br />
name: The name of the file want to be opened.<br />
flags: The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
return value: A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
e.g: fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
void cfs_close(int fd)<br />
Close a open file.<br />
fd: The file descriptor of the open file.<br />
e.g: cfs_close(fd)<br />
Close the file with file descriptor fd.<br />
<br />
int cfs_read(int fd, void* buf, unsigned int len)<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
fd: The file descriptor of the open file.<br />
buf: The buffer in which data should be read from the file.<br />
len: The number of bytes that should be read.<br />
Return Value: The number of bytes that was actually read from the file or -1 if an error happens<br />
e.g: ret = cfs_read(fd, buf, sizeof(buf))<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
int cfs_write(int fd, const void *buf, unsigned int len)<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
fd: The file descriptor of the open file.<br />
buf: The buffer from which data should be written to the file.<br />
len: The number of bytes that should be written.<br />
Return Value: The number of bytes that was actually written to the file.<br />
e.g: ret = cfs_write(fd, buf, sizeof(buf))<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was acutually written to the file.<br />
<br />
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
fd: The file descriptor of the open file.<br />
offset: A position, either relative or absolute, in the file.<br />
whence: Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
Return Value: The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
e.g: ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
Move the file position to the beginning of the file.<br />
<br />
int cfs_remove(const char *name)<br />
Remove a file with name name.<br />
name: The name of the file.<br />
Return Value: 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
e.g: ret = sfc_remove(name)<br />
Remove the file with name pointed by name.<br />
<br />
int cfs_opendir(struct cfs_dir *dirp, const char *name)<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
dirp: A pointer to a struct cfs_dir that is filled in by the function.<br />
name: The name of the directory.<br />
Return Value: 0 or -1 if the directory could not be opened.<br />
e.g: ret = cfs_opendir(&dir, "/")<br />
Open the root directory.<br />
<br />
int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
dirent: A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
Return Value: 0 or -1 if no more directory entries can be read.<br />
e.g: if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
void cfs_closedir(struct cfs_dir *dirp)<br />
Close the dirctory dirp opened with cfs_opendir().<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
int cfs_coffee_format(void)<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_reserve(const char *name, cfs_offset_t size)<br />
Reserve space for a file of name with size.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size)<br />
Configure the on-demand log file.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=250CFS-Coffee2014-02-28T03:44:51Z<p>Nikhil: </p>
<hr />
<div>==Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) ==<br />
<br />
==Introduction==<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
==The CFS Programming Interface==<br />
<br />
int cfs_open(const char* name, int flags)<br />
Open a file with specific flags. <br />
name: The name of the file want to be opened.<br />
flags: The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
return value: A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
e.g: fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
void cfs_close(int fd)<br />
Close a open file.<br />
fd: The file descriptor of the open file.<br />
e.g: cfs_close(fd)<br />
Close the file with file descriptor fd.<br />
<br />
int cfs_read(int fd, void* buf, unsigned int len)<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
fd: The file descriptor of the open file.<br />
buf: The buffer in which data should be read from the file.<br />
len: The number of bytes that should be read.<br />
Return Value: The number of bytes that was actually read from the file or -1 if an error happens<br />
e.g: ret = cfs_read(fd, buf, sizeof(buf))<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
int cfs_write(int fd, const void *buf, unsigned int len)<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
fd: The file descriptor of the open file.<br />
buf: The buffer from which data should be written to the file.<br />
len: The number of bytes that should be written.<br />
Return Value: The number of bytes that was actually written to the file.<br />
e.g: ret = cfs_write(fd, buf, sizeof(buf))<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was acutually written to the file.<br />
<br />
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
fd: The file descriptor of the open file.<br />
offset: A position, either relative or absolute, in the file.<br />
whence: Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
Return Value: The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
e.g: ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
Move the file position to the beginning of the file.<br />
<br />
int cfs_remove(const char *name)<br />
Remove a file with name name.<br />
name: The name of the file.<br />
Return Value: 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
e.g: ret = sfc_remove(name)<br />
Remove the file with name pointed by name.<br />
<br />
int cfs_opendir(struct cfs_dir *dirp, const char *name)<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
dirp: A pointer to a struct cfs_dir that is filled in by the function.<br />
name: The name of the directory.<br />
Return Value: 0 or -1 if the directory could not be opened.<br />
e.g: ret = cfs_opendir(&dir, "/")<br />
Open the root directory.<br />
<br />
int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
dirent: A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
Return Value: 0 or -1 if no more directory entries can be read.<br />
e.g: if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
void cfs_closedir(struct cfs_dir *dirp)<br />
Close the dirctory dirp opened with cfs_opendir().<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
==Introduction to Coffee==<br />
<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
==CFS-Coffee Interface Extensions==<br />
<br />
int cfs_coffee_format(void)<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_reserve(const char *name, cfs_offset_t size)<br />
Reserve space for a file of name with size.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size)<br />
Configure the on-demand log file.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
<br />
<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=CFS-Coffee&diff=249CFS-Coffee2014-02-28T03:41:49Z<p>Nikhil: Created page with "Permanent Storage of Contiki on Tmote Sky (CFS-Coffee) Introduction Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works..."</p>
<hr />
<div>Permanent Storage of Contiki on Tmote Sky (CFS-Coffee)<br />
<br />
Introduction<br />
<br />
Contiki provides File System to achieve the goal of permanent storage. The Contiki File System(CFS) works as a Virtual File System and provides some interfaces for different file systems. CFS-POSIX and Coffee are two with full functionalities. CFS-POSIX is used in Contiki platforms that runs in native mode. Coffee, on the other hand, is primarily aimed at sensor devices that equiped with Flash Memories or EEPROM.<br />
<br />
The CFS Programming Interface<br />
<br />
int cfs_open(const char* name, int flags)<br />
Open a file with specific flags. <br />
name: The name of the file want to be opened.<br />
flags: The way the file will be opened: CFS_READ, CFS_WRITE, CFS_APPEND or their combiantion.<br />
return value: A file descriptor, if the file could be opened, or -1 if the file could not be opened.<br />
e.g: fd = cfs_open(“Contiki_Group”, CFS_READ | CFS_WRITE | CFS_APPEND)<br />
Open a file called “Contiki_Group” which allow both to read and to write. Notice the “CFS_WRITE | CFS_APPEND” means append to the file. Only make use of CFS_APPEND will not achieve this goal. CFS_WRITE has to be included.<br />
<br />
void cfs_close(int fd)<br />
Close a open file.<br />
fd: The file descriptor of the open file.<br />
e.g: cfs_close(fd)<br />
Close the file with file descriptor fd.<br />
<br />
int cfs_read(int fd, void* buf, unsigned int len)<br />
Read data from a open file and fills buf with at most len bytes, starting from the current position in the file descriptor.<br />
fd: The file descriptor of the open file.<br />
buf: The buffer in which data should be read from the file.<br />
len: The number of bytes that should be read.<br />
Return Value: The number of bytes that was actually read from the file or -1 if an error happens<br />
e.g: ret = cfs_read(fd, buf, sizeof(buf))<br />
Read from file with file descriptor fd and fill buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that read from the file or -1 if an error happens.<br />
<br />
int cfs_write(int fd, const void *buf, unsigned int len)<br />
Write len bytes from the memory buffer buf into the file, starting from the current position in the file descriptor.<br />
fd: The file descriptor of the open file.<br />
buf: The buffer from which data should be written to the file.<br />
len: The number of bytes that should be written.<br />
Return Value: The number of bytes that was actually written to the file.<br />
e.g: ret = cfs_write(fd, buf, sizeof(buf))<br />
Write to file with file descriptor fd from buffer buf with at most its size. Here buf is a pointer. ret contains the numbers of bytes that was acutually written to the file.<br />
<br />
cfs_offset_t cfs_seek(int fd, cfs_offset_t offset, int whence)<br />
Move the current file position to the position determined by the combination of the offset and the whence.<br />
fd: The file descriptor of the open file.<br />
offset: A position, either relative or absolute, in the file.<br />
whence: Determines how to interpret the offset parameter. CFS_SEEK_SET computes the offset from the beginning of the file; CFS_SEEK_CUR computes the offset from the current position of the file position; CFS_SEEK_END computes the offset in relation to the end of the file. Negative offset values are accepted by both CFS_SEEK_CUR and CFS_SEEK_END. <br />
Return Value: The new absolute file position upon success, or -1 if the file pointer could not be moved to the requested position.<br />
e.g: ret = cfs_seek(fd, 0, CFS_SEEK_SET)<br />
Move the file position to the beginning of the file.<br />
<br />
int cfs_remove(const char *name)<br />
Remove a file with name name.<br />
name: The name of the file.<br />
Return Value: 0 if the file was removed or -1 if the file could not be removed or if it doesn’t exist.<br />
e.g: ret = sfc_remove(name)<br />
Remove the file with name pointed by name.<br />
<br />
int cfs_opendir(struct cfs_dir *dirp, const char *name)<br />
Open the directory name and fills in an opaque handle pointed to by dirp. The contents of this handle is unspecified and is only for use internally by CFS implementation.<br />
struct cfs_dir {<br />
char dummy_space[32];<br />
}<br />
dirp: A pointer to a struct cfs_dir that is filled in by the function.<br />
name: The name of the directory.<br />
Return Value: 0 or -1 if the directory could not be opened.<br />
e.g: ret = cfs_opendir(&dir, "/")<br />
Open the root directory.<br />
<br />
int cfs_readdir(struct cfs_dir *dirp, struct cfs_dirent *dirent)<br />
Read one entry of directroy dirp at a time. Write the directory entry into the space pointed to by dirent. dirent should be preallocated.<br />
struct cfs_dirent {<br />
char name[32];<br />
cfs_offset_t size;<br />
}<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
dirent: A pointer to a struct cfs_dirent that is filled in by cfs_readdir().<br />
Return Value: 0 or -1 if no more directory entries can be read.<br />
e.g: if (cfs_opendir(&dir, “/”) == 0) {<br />
while(cfs_readdir(&dir, &dirent) != -1) {<br />
printf(“File: %s (%ld bytes)\n”, dirent.name, (long)dirent.size);<br />
}<br />
cfs_closedir(&dir);<br />
}<br />
The first line opens the root directory. If it successes, the while() in the second line reads all the entries in root directory and prints the name and size of those entries. After that, close the root directory.<br />
<br />
void cfs_closedir(struct cfs_dir *dirp)<br />
Close the dirctory dirp opened with cfs_opendir().<br />
dirp: A pointer to a struct cfs_dir that has been opened with cfs_opendir().<br />
<br />
Introduction to Coffee<br />
Coffee is a fully functional file system designed specifically for the characteristics of flash memories and EEPROM. Coffee is usually used in sensor devices with flash (e.g Tmote Sky), so two principles are pretty important. First, the metadata stored in RAM for each file should be small because sensor devices have very limited RAM space. Second, bits in flash memories can be toggled from 1 to 0, but not toggled back from 0 to 1 without doing an expensive erase. Thus, when modifying a file, Coffee creates an invisible micro log which is linked to the original file. The micro log contains the lastest data of the file. For user, the micro log and the original file is just one logical file, as if they modify the original file. When the micro log eventually fills up, Coffee transparently merges the content of the original file and the micro log into a new file, and deletes the two former files. <br />
Coffee has a flat directory structure. It only has the root directory. Thus, cfs_opendir() only accept “/” or “.” as the second argument. When removing a file from a Coffee, there are two steps. First, external user calls cfs_remove(). The file mentioned by user will be marked as obsolete. Obsolete files cannot be seen by external users. Coffee will actually delete files only when a new file reservation request cannot be granted.<br />
The implementation of Coffee in core/cfs/cfs-coffee.c is totally platform independent. The specific configuration of Coffee for different platforms is written in csf-coffee-arch.c. We can see many csf-coffee-arch.c files in plarform/./csf-coffee-arch.c, e.g plarform/z1/csf-coffee-arch.c, plarform/esb/csf-coffee-arch.c, plarform/sky/csf-coffee-arch.c. When we decide the platform, such as TARGET=sky, we compile plarform/sky/csf-coffee-arch.c and ignore all other csf-coffee-arch.c files. Macro definitions in plarform/sky/csf-coffee-arch.c configure the details of coffee in Sky Mote. Macro definition contains parameters like COFFEE_SECTOR_SIZE, COFFEE_PAGE_SIZE, COFFEE_FD_SET_SIZE, COFFEE_MICRO_LOGS and so on. Also, it defines COFFEE_WRITE, COFFEE_READ and COFFEE_ERASE to point to the device drivers I/O function. <br />
<br />
CFS-Coffee Interface Extensions<br />
<br />
int cfs_coffee_format(void)<br />
Coffee formats the underlying storage by setting all bits to zero. Formatting must be done before using Coffee for the first time in a mote.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_reserve(const char *name, cfs_offset_t size)<br />
Reserve space for a file of name with size.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
int cfs_coffee_configure_log(const char *file, unsigned log_size, unsigned log_entry_size)<br />
Configure the on-demand log file.<br />
Return Value: 0 on success, -1 on failure.<br />
<br />
cfs_coffee_format() may take a few second because all sectors must be erased. cfs_coffee_reserve() and int cfs_coffee_configure_log() can optimize Coffee’s handling of the file.<br />
<br />
Here is an example of how everything works. Suppose we want to run Contiki on Sky Mote. First make sure TARGET=sky. Then every file about sky is compiled and you can also see a new folder which is created called sky_obj. Inside contains everything you need to run on Sky Mote. Suppose you want to use Coffee as the File System. Write cfs_coffee_format() before the first time you try to operate on Coffee. This function notice the system that you want to use Coffee and the system will do some formatting. Let’s say then you want to read something from an existing file with the function call cfs_read(). This function is defined in core/cfs/cfs.h. Because we make use of Coffee, the implementation of this function will be in core/cfs/cfs-coffee.c. The function cfs_read() actually calls the macro definition COFFEE_READ(). The definition of COFFEE_READ() is in cfs-coffee-arch.c. Since we make TARGET=sky, the one that was compiled and used by us is platform/sky/cfs-coffee-arch.c. This file defines COFFEE_READ() to point to Sky Mote drivers I/O function. Finally, the external users’ cfs_read() gets to the Sky Driver I/O function.<br />
<br />
An Example of Running Coffee on Tmote Sky<br />
This example shows the basic way to open, read, write and append files in Coffee. The platform we choose here is Tmote Sky.<br />
First, open an example provided by Contiki (/home/user/contiki-2.7/example/sky/example- coffee.c).<br />
Here, it defines two test functions:<br />
static int file_test(const char *filename, char *msg) opens a file named FILENAME with CFS_WRITE | CFS_APPEND | CFS_READ. It appends the opening file with records and prints out everything in the file.<br />
static int dir_test(void) opens the root directory and prints out all entries of root.<br />
The Process calls file_test() twice and dir_test() once. Then the process ends.<br />
Now, we do some modifications based on this example. First we add a function static int read_test(void). The implementation of this function is shown as below:<br />
static int<br />
read_test(void)<br />
{<br />
struct cfs_dir dir;<br />
struct cfs_dirent dirent;<br />
<br />
int fd;<br />
int r;<br />
<br />
struct record {<br />
char message[16];<br />
uint8_t sequence;<br />
} record;<br />
<br />
static int i = 0;<br />
if (cfs_opendir(&dir, "/") != 0)<br />
{<br />
printf("Fail to open the root directory\n");<br />
return -1;<br />
}<br />
while(cfs_readdir(&dir, &dirent) == 0)<br />
{<br />
i ++;<br />
printf("\nFile name: %s\n", dirent.name);<br />
if ((fd =cfs_open(dirent.name, CFS_READ)) != -1)<br />
{<br />
cfs_seek(fd, 0, CFS_SEEK_SET);<br />
for(;;) <br />
{<br />
r = cfs_read(fd, &record, sizeof(record));<br />
if(r == 0) {<br />
break;<br />
} else if(r > sizeof(record)) {<br />
printf("failed to read %d bytes from %s, got %d\n",(int)sizeof(record), dirent.name, r);<br />
cfs_close(fd);<br />
return -1;<br />
}<br />
<br />
printf("Read message \"%s\", sequence %u\n",record.message, record.sequence);<br />
}<br />
}<br />
}<br />
<br />
if (i == 0)<br />
{<br />
printf("\nNo file exists.\n");<br />
return 0;<br />
}<br />
else<br />
{<br />
return 1;<br />
}<br />
<br />
}<br />
<br />
The function of read_test() is quite simple. It opens the root file system and reads all the data in the file system. In the while() loop, it reads an entry of a file. Then it makes use of the entry to open the file and read all the data out.<br />
Also, in the process, we add an etimer at the beginning. The process can only move on either when the timer expired or someone push a button. If the timer expire, the process will run cfs_coffee_format(), which will set all the flash to 1. Otherwise, we don’t do this. Then we call read_test(), which read all the data in the file system. After that, we check whether there are files exist in the system. If there is, we give up wirting. Otherwise, we create two files and write some data in.<br />
The process then looks like:<br />
PROCESS_THREAD(example_coffee_process, ev, data)<br />
{<br />
PROCESS_BEGIN();<br />
<br />
static int flag = 0;<br />
button_sensor.configure(SENSORS_ACTIVE, 1);<br />
<br />
static struct etimer ak;<br />
etimer_set(&ak, CLOCK_SECOND * 6);<br />
<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER || ev == sensors_event && data == &button_sensor);<br />
<br />
if (ev == PROCESS_EVENT_TIMER)<br />
cfs_coffee_format(); <br />
<br />
if ((flag = read_test()) == -1)<br />
PROCESS_EXIT();<br />
<br />
<br />
static struct etimer et;<br />
etimer_set(&et, CLOCK_SECOND * 2);<br />
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);<br />
<br />
if (flag == 1)<br />
printf("\nData exists...No written again...\n");<br />
else<br />
{<br />
printf("\nData begin to write...\n");<br />
if(file_test(FILENAME_1, "The first test") == 0) {<br />
printf("file test 1 failed\n");<br />
}<br />
if(file_test(FILENAME_1, "The second test") == 0) {<br />
printf("file test 2 failed\n");<br />
}<br />
if(file_test(FILENAME_2, "The third test") == 0) {<br />
printf("file test 3 failed\n");<br />
}<br />
if(dir_test() == 0) {<br />
printf("dir test failed\n");<br />
}<br />
<br />
}<br />
<br />
PROCESS_END();<br />
}<br />
Now, connect your mote to your PC’s USB port and change directory. <br />
cd contiki-2.7/examples/sky <br />
<br />
Compile this Coffee example to Sky Mote.<br />
sudo make TARGET=sky example-coffee.upload login<br />
<br />
Once login, wait for 6 seconds patiently for the first time out.If it success, we should see terminal has the output like this:<br />
Here you can see, we create two files and write some data in. Then, disconnect your mote and do whatever you want. Maybe one day later, you come back. Now it’s the time to keep moving. <br />
Welcome back. Now connect your mote with USB port again and do:<br />
sudo make TARGET=sky login<br />
You are in the process again and please follow the following two steps:<br />
1. push “RESET” button;<br />
2. push “USER” button within 6 seconds.<br />
Now, the program will not call cfs_coffee_format(). If succeed, you will see: <br />
As you can see, we read out all the data that you wrote in one day ago. Everything is right there without any change.</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Process&diff=247Process2014-02-22T03:55:46Z<p>Nikhil: Blanked the page</p>
<hr />
<div></div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Contiki_tutorials&diff=246Contiki tutorials2014-02-22T01:26:20Z<p>Nikhil: /* List of Tutorials */</p>
<hr />
<div>[[Main_Page | Back to Main Page]]<br />
<br />
== List of Tutorials ==<br />
<br />
Completed<br />
<br />
# [[Installation]]<br />
# [[Broadcast Example]]<br />
# [[Collect View]]<br />
<br />
Need review<br />
<br />
# [[Hello World]]<br />
<br />
Starting<br />
# [[Tutornet]]<br />
# [[Timers]]<br />
# [[Cooja Simulator]]<br />
# [[Contiki build system]]<br />
# [[Processes]]<br />
# [[Protocols stack]]<br />
# [[CFS-Coffee]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Collect_View&diff=245Collect View2014-02-22T01:25:35Z<p>Nikhil: </p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
== Introduction ==<br />
<br />
This exercise is to familiarize the user with the collect view app provided in the Contiki OS source code. It basically involves a sink mote and one or more than one source motes. The source motes send their sensor, network, power and node properties to the sink. A java app presents all this compiled data in a graphical manner.<br />
<br />
== You Will Learn ==<br />
<br />
* How to upload an introductory code for UDP source and sink.<br />
* How to program nodes and collect data through the collect-view GUI.<br />
* Things to see on the collect-view GUI.<br />
<br />
== Relevant Directories ==<br />
<br />
* /contiki/examples/collect/ - This has the shell code for collect view and the various targets.<br />
* /contiki/examples/ipv6/rpl-collect/ - This has the codes which are to be uploaded to the source and sink motes for UDP communication.<br />
* /contiki/tools/collect-view/ - This contains the java file for the collect view GUI.<br />
* /contiki/apps/collect-view/ - This contains the codes corresponding to the targets mentioned in the examples folder.<br />
<br />
== Connect the Tmote Sky ==<br />
<br />
Before continuing with the tutorial, plug 2 Tmote Sky into the computer's USB port, one for the source and the other for the sink.<br />
<br />
In order to transfer the ownership of the contiki directories use the following command. <br />
<br />
<code><nowiki> sudo chown :user /dev/ttyUSB0 </nowiki></code><br />
<br />
<code><nowiki> sudo chown :user /dev/ttyUSB1 </nowiki></code><br />
<br />
== Using the Source Code ==<br />
<br />
=== Step 1 ===<br />
<br />
Go to /examples/ipv6/rpl-collect/ directory.<br />
<br />
=== Step 2 ===<br />
<br />
Now we need to program one mote as the sink and the other as source. We will use a predefined target called z1 to upload the code in the motes.<br />
<br />
For sink:<br />
<br />
<code><nowiki> make TARGET=z1 MOTES=/dev/ttyUSB0 udp-sink.upload </nowiki></code><br />
<br />
For source:<br />
<br />
<code><nowiki> make TARGET=z1 MOTES=/dev/ttyUSB1 udp-sender.upload </nowiki></code><br />
<br />
=== Step 3 ===<br />
<br />
Now we would like to see the transfer and depiction of data on the collect-view GUI. For that to happen, go to /tools/collect-view directory and run "ant" to build the collect viewer.<br />
<br />
<code><nowiki> ant run </nowiki></code><br />
<br />
'''Note''' - In order to edit GUI java files go to the /src directory.<br />
<br />
=== Step 4 ===<br />
<br />
To view the data collected at the sink, go to the generated /tools/collect-view/dist and run - <br />
<br />
<code><nowiki> java -jar collect-view-jar /dev/ttyUSB0 </nowiki></code><br />
<br />
=== Step 5 ===<br />
<br />
In the collect window from the Tools menu, select "Run Init script" and in the "Node Control" tab and click on "Start Collect." This will enable the communication between the source and the sender and you will be able to see the received statistics in a graphical manner.<br />
<br />
== Using the GUI ==<br />
<br />
=== Step 1 ===<br />
<br />
Once the motes are connected to the USB ports, go to /tools/collect-view directory and run "ant" to build the collect viewer.<br />
<br />
<code><nowiki> ant run </nowiki></code><br />
<br />
=== Step 2 ===<br />
<br />
Press the "Program Nodes..." button as seen on the GUI.<br />
<br />
=== Step 3 ===<br />
<br />
Disconnect all except one node from the USB ports. Press the "Connect to serial" button. The disconnected motes should be connected to independent power sources.<br />
<br />
=== Step 3 ===<br />
<br />
Now in order to collect various statistics, press "Send Collect" button.<br />
<br />
=== Step 4 ===<br />
<br />
Set the various "Collect Settings" based on how frequently the data is to be collected and then press "Send command to nodes" button.<br />
<br />
=== Step 5 ===<br />
<br />
Now go through the other tabs on the GUI to study and analyze the statistics plotted.<br />
<br />
== Things to See on the GUI ==<br />
<br />
=== Topological graphs ===<br />
<br />
* Sensor Map<br />
* Network Graph<br />
<br />
=== Sensor Related Plots ===<br />
<br />
These are subject to the availability of a particular type of sensor on the motes.<br />
<br />
* Temperature Sensor - Average Temperatue and Temperature.<br />
* Humidity Sensor - Relative Humidity.<br />
* Battery Sensor - Battery Indicator and Battery Voltage.<br />
* Light Sensor - Light 1 and Light 2.<br />
<br />
=== Network Metrics Related Plots ===<br />
<br />
* Neighbours<br />
* Beacon Interval<br />
* Network Hops <br />
** Over Time<br />
** Per Node<br />
* Router Metric (Over Time)<br />
** Instantaneous<br />
** Average<br />
* ETX (Over Time)<br />
* Next Hop (Over Time)<br />
* Latency<br />
* Lost Packets (Over Time)<br />
* Received Packets<br />
** Over Time<br />
** Per Node<br />
** Every 5 min<br />
<br />
=== Power Related Plots ===<br />
<br />
* Average Power<br />
* Instantaneous Power<br />
* Power History<br />
* Radio Duty Cycle<br />
<br />
=== Other Tabs ===<br />
<br />
* Node Info - This gives a summary of all the nodes and their statistics.<br />
* Serial Console - The user can interact directly with the mote, sending commands to read and transmit data.<br />
<br />
<br />
'''Primary contributors:''' Bhavna, Deepanker, Nikhil, Rahul<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
<br />
Edited by: Deepanker</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Collect_View&diff=188Collect View2014-02-14T19:47:00Z<p>Nikhil: </p>
<hr />
<div>== Introduction ==<br />
<br />
Contiki's Collect View App allows a user to see the sensor data from a Sky Mote. This tutorial will give you step-by-step instructions on how to run Collect View using two Sky Motes. <br />
<br />
== You Will Learn ==<br />
<br />
* How to program motes within Collect View<br />
* The GUI of Collect View<br />
<br />
== Tutorial ==<br />
<br />
=== Step 1 ===<br />
<br />
Plug two Sky Motes into two USB ports on your computer: ttyUSB0 and ttyUSB1. Within Contiki, you will need to gain administrative access into these motes before you can run Collect View. Type the following code into terminal. <br />
<br />
sudo chown :user /dev/ttyUSB0<br />
sudo chown :user /dev/ttyUSB1<br />
<br />
Note: This step must be repeated twice for each USB port<br />
<br />
=== Step 2 ===<br />
<br />
Navigate to the Collect View directory (cd contiki/tools/collect-view/) and build the collect viewer with the following command: <br />
<br />
run ant</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Collect_View&diff=187Collect View2014-02-14T19:34:14Z<p>Nikhil: Created page with "== Introduction == Contiki's Collect View App allows a user to see the sensor data from a Sky Mote. This tutorial will give you step-by-step instructions on how to run Collec..."</p>
<hr />
<div>== Introduction ==<br />
<br />
Contiki's Collect View App allows a user to see the sensor data from a Sky Mote. This tutorial will give you step-by-step instructions on how to run Collect View using two Sky Motes. <br />
<br />
== You Will Learn ==<br />
<br />
* How to program motes within Collect View<br />
* The GUI of Collect View<br />
<br />
== Tutorial ==<br />
<br />
=== Step 1 ===</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Process&diff=184Process2014-02-11T05:40:47Z<p>Nikhil: </p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
In Contiki, an application contains a process, also known as an event handler, <br />
<br />
__TOC__<br />
<br />
= Description =<br />
<br />
The kernel process module (i.e., found in process.c) is the heart of the OS. It contains a number of APIs for manipulating processes, events and scheduling. There are two main data objects that exist here, the Process List and the FIFO Asynchronous Event Queue.<br />
<br />
The Process List is implemented as a singly linked list of process structures. This list only contains processes structures for processes that have been started in the system. These could either be processes that were started when the OS bootstrapped or were dynamically started during the run-time of the system. The length of the Process List can grow and shrink at run-time as new processes are started and/or older processes are stopped.<br />
<br />
The FIFO Asynchronous Event Queue is implemented as a ring buffer whose length is fixed at compile time, through a configurable constant. The asynchronous events in this queue can be either of the single receiver or broadcast type. Note that synchronous events are never put into this queue as they are dispatched immediately. The queue holds events that have not yet been dispatched. They will be scheduled at a later time when the scheduler is invoked.<br />
<br />
= Process Structure =<br />
<br />
= Protothreads =</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Process&diff=180Process2014-02-11T05:25:11Z<p>Nikhil: /* Protothreads */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
The kernel process module (i.e., found in process.c) is the heart of the OS. It contains a number of APIs for manipulating processes, events and scheduling. There are two main data objects that exist here, the Process List and the FIFO Asynchronous Event Queue.<br />
<br />
The Process List is implemented as a singly linked list of process structures. This list only contains processes structures for processes that have been started in the system. These could either be processes that were started when the OS bootstrapped or were dynamically started during the run-time of the system. The length of the Process List can grow and shrink at run-time as new processes are started and/or older processes are stopped.<br />
<br />
The FIFO Asynchronous Event Queue is implemented as a ring buffer whose length is fixed at compile time, through a configurable constant. The asynchronous events in this queue can be either of the single receiver or broadcast type. Note that synchronous events are never put into this queue as they are dispatched immediately. The queue holds events that have not yet been dispatched. They will be scheduled at a later time when the scheduler is invoked.<br />
<br />
__TOC__<br />
<br />
= Process Structure =<br />
<br />
= Protothreads =</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Process&diff=179Process2014-02-11T05:24:50Z<p>Nikhil: </p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
The kernel process module (i.e., found in process.c) is the heart of the OS. It contains a number of APIs for manipulating processes, events and scheduling. There are two main data objects that exist here, the Process List and the FIFO Asynchronous Event Queue.<br />
<br />
The Process List is implemented as a singly linked list of process structures. This list only contains processes structures for processes that have been started in the system. These could either be processes that were started when the OS bootstrapped or were dynamically started during the run-time of the system. The length of the Process List can grow and shrink at run-time as new processes are started and/or older processes are stopped.<br />
<br />
The FIFO Asynchronous Event Queue is implemented as a ring buffer whose length is fixed at compile time, through a configurable constant. The asynchronous events in this queue can be either of the single receiver or broadcast type. Note that synchronous events are never put into this queue as they are dispatched immediately. The queue holds events that have not yet been dispatched. They will be scheduled at a later time when the scheduler is invoked.<br />
<br />
__TOC__<br />
<br />
= Process Structure =<br />
<br />
=== Protothreads ===</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Process&diff=178Process2014-02-11T05:23:15Z<p>Nikhil: </p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
The kernel process module (i.e., found in process.c) is the heart of the OS. It contains a number of APIs for manipulating processes, events and scheduling. There are two main data objects that exist here, the Process List and the FIFO Asynchronous Event Queue.<br />
<br />
The Process List is implemented as a singly linked list of process structures. This list only contains processes structures for processes that have been started in the system. These could either be processes that were started when the OS bootstrapped or were dynamically started during the run-time of the system. The length of the Process List can grow and shrink at run-time as new processes are started and/or older processes are stopped.<br />
<br />
The FIFO Asynchronous Event Queue is implemented as a ring buffer whose length is fixed at compile time, through a configurable constant. The asynchronous events in this queue can be either of the single receiver or broadcast type. Note that synchronous events are never put into this queue as they are dispatched immediately. The queue holds events that have not yet been dispatched. They will be scheduled at a later time when the scheduler is invoked.<br />
<br />
__TOC__<br />
<br />
=== Process Structure ===<br />
<br />
=== Protothreads ===</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Process&diff=177Process2014-02-11T05:15:52Z<p>Nikhil: Created page with " Back to Contiki Tutorials The kernel process module (i.e., found in process.c) is the heart of the OS. It contains a number of APIs for manipulating ..."</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
The kernel process module (i.e., found in process.c) is the heart of the OS. It contains a number of APIs for manipulating processes, events and scheduling. There are two main data objects that exist here, the Process List and the FIFO Asynchronous Event Queue.<br />
<br />
The Process List is implemented as a singly linked list of process structures. This list only contains processes structures for processes that have been started in the system. These could either be processes that were started when the OS bootstrapped or were dynamically started during the run-time of the system. The length of the Process List can grow and shrink at run-time as new processes are started and/or older processes are stopped.<br />
<br />
The FIFO Asynchronous Event Queue is implemented as a ring buffer whose length is fixed at compile time, through a configurable constant. The asynchronous events in this queue can be either of the single receiver or broadcast type. Note that synchronous events are never put into this queue as they are dispatched immediately. The queue holds events that have not yet been dispatched. They will be scheduled at a later time when the scheduler is invoked.<br />
<br />
__TOC__<br />
<br />
=== Process Structure ===</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=162Broadcast Example2014-02-08T00:49:42Z<p>Nikhil: /* Macros and Structures */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1 ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2 ===<br />
<br />
Next, we will edit the data in the example-broadcast.c file. In order to do this, you will need to open the file with your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type the following into the terminal:<br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3 ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains: <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4 ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5 ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
<br />
<br />
<br />
<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. A glance at core/net/rime/broadcast.h and core/net/rime/broadcast.c helps in the understanding underlying concepts of this example.<br />
In the code, a process called example_broadcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
==== Macros and Structures ====<br />
<br />
<code><nowiki> PROCESS(name,strname) </nowiki></code><br />
<br />
Every process should be defined via the PROCESS macro.<br />
PROCESS has two arguments: the variable of the process structure, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
<code><nowiki> AUTOSTART_PROCESS(struct process &) </nowiki></code><br />
<br />
AUTOSTART_PROCESSES automatically starts the process(es) given in the argument(s) when the module boots.<br />
<br />
*&name: Reference to the process name<br />
<br />
<code><nowiki> broadcast_recv(struct broadcast_conn *, const rimeaddr_t *) </nowiki></code><br />
<br />
This function parses an incoming packet and displays the message and the address of the sender.<br />
By setting it as the broadcast's designated callback function, broadcast_recv is automatically called when a packet is received.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to recv and sent functions (in this example, just recv).<br />
<br />
*rimeaddr_t *: This is a union which has a character array u8[RIMEADDR_SIZE].<br />
<br />
<code><nowiki> PROCESS_THREAD(name, process_event_t, process_data_t) </nowiki></code><br />
<br />
A process in Contiki consists of a single reference to "protothread". This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. <br />
Each process in the module requires an event handler under the PROCESS_THREAD macro.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
Within the body of PROCESS_THREAD there are 3 major tasks:<br />
* Initialization<br />
** allocate resources<br />
** define variables<br />
** begin process<br />
* Infinite Loop<br />
** while(1) is used to create an infinite loop in which the actual event-handling response takes place<br />
* Deallocation<br />
** end process<br />
** deallocate resources<br />
<br />
<br />
<br />
<code><nowiki>PROCESS_EXITHANDLER(handler) </nowiki></code><br />
<br />
Specifies an action when a process exits.<br />
NOTE: This declaration must come immediately before the PROCESS_BEGIN() macro.<br />
<br />
* handler: The action to be performed.<br />
<br />
<br />
<br />
<code><nowiki>PROCESS_BEGIN() </nowiki></code><br />
<br />
This macro defines the beginning of a process, and must always appear in a PROCESS_THREAD() definition.This macro initiates PT_BEGIN(), which is declared at the starting point of a protothread. All C statements above the PT_BEGIN() invokation will be executed each time the protothread is scheduled.<br />
<br />
<br />
<br />
<code><nowiki>broadcast_close(struct broadcast_conn *) </nowiki></code><br />
<br />
This function closes a broadcast connection that has previously been opened with broadcast_open().This function typically is called as an exit handler.<br />
<br />
*broadcast_conn : This is same as the variable from boradcast_recv().<br />
<br />
<br />
<br />
<code><nowiki> PROCESS_END() </nowiki></code><br />
<br />
This macro defines the end of a process. It must appear in a PROCESS_THREAD() definition and must always be included. The process exits when the PROCESS_END() macro is reached. This macro initiates PT_END().It must always be used together with a matching PT_BEGIN() macro.<br />
<br />
<br />
<br />
<code><nowiki> broadcast_open(struct broadcast_conn *, uint16_t ,const struct broadcast_callbacks *) </nowiki></code><br />
<br />
Sets up an identified best-effort broadcast connection. The caller will allocate the memory for the struct broadcast_conn, usually by declaring it as a static variable. The struct broadcast_callbacks pointer points to a structure containing a pointer to a function that will be called when a packet arrives on the channel. This function opens a connection of type abc_conn and sets the callbacks to structure passed. Also, this points to channel_set_attributes() function.<br />
<br />
*broadcast_conn : A pointer to a struct broadcast_conn<br />
*uint16_t: The channel on which the connection will operate<br />
*broadcast_callbacks : A struct broadcast_callbacks with function pointers to functions that will be called when a packet has been received<br />
<br />
<br />
<br />
<code><nowiki> etimer_set(struct etimer *, clock_time_t) </nowiki></code><br />
<br />
This function is used to set an event timer for a time sometime in the future. When the event timer expires, the event PROCESS_EVENT_TIMER will be posted to the process that called the etimer_set() function.<br />
<br />
*etimer : A pointer to the event timer<br />
*clock_time_t : The interval before the timer expires.<br />
<br />
<br />
<br />
<code><nowiki> static struct broadcast_conn </nowiki></code><br />
<br />
The broadcast module sends a packet to all local area neighbors with a header that identifies the sender. It also adds a single-hop address as a packet attribute to out going packets. <br />
broadcast_conn structure consists of two structures<br />
*abc_conn struct: the abc(Anonymous best effort local area Broadcast) module sends packets to all local area neighbors. It uses one channel.<br />
*broadcast_callbacks struct: this is called when a packet has been received by the broadcast module. The struct broadcast_callbacks pointer is used in broadcast_open to point to a function that will be called when a packet arrives on the channel.<br />
<br />
<br />
<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=161Broadcast Example2014-02-08T00:48:33Z<p>Nikhil: /* Macros and Structures */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1 ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2 ===<br />
<br />
Next, we will edit the data in the example-broadcast.c file. In order to do this, you will need to open the file with your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type the following into the terminal:<br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3 ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains: <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4 ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5 ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
<br />
<br />
<br />
<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. A glance at core/net/rime/broadcast.h and core/net/rime/broadcast.c helps in the understanding underlying concepts of this example.<br />
In the code, a process called example_broadcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
==== Macros and Structures ====<br />
<br />
<code><nowiki> PROCESS(name,strname) </nowiki></code><br />
<br />
Every process should be defined via the PROCESS macro.<br />
PROCESS has two arguments: the variable of the process structure, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
<code><nowiki> AUTOSTART_PROCESS(struct process &) </nowiki></code><br />
<br />
AUTOSTART_PROCESSES automatically starts the process(es) given in the argument(s) when the module boots.<br />
<br />
*&name: Reference to the process name<br />
<br />
<code><nowiki> broadcast_recv(struct broadcast_conn *, const rimeaddr_t *) </nowiki></code><br />
<br />
This function parses an incoming packet and displays the message and the address of the sender.<br />
By setting it as the broadcast's designated callback function, broadcast_recv is automatically called when a packet is received.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to recv and sent functions (in this example, just recv).<br />
<br />
*rimeaddr_t *: This is a union which has a character array u8[RIMEADDR_SIZE].<br />
<br />
<code><nowiki> PROCESS_THREAD(name, process_event_t, process_data_t) </nowiki></code><br />
<br />
A process in Contiki consists of a single reference to "protothread". This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. <br />
Each process in the module requires an event handler under the PROCESS_THREAD macro.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
Within the body of PROCESS_THREAD there are 3 major tasks:<br />
* Initialization<br />
** allocate resources<br />
** define variables<br />
** begin process<br />
* Infinite Loop<br />
** while(1) is used to create an infinite loop in which the actual event-handling response takes place<br />
* Deallocation<br />
** end process<br />
** deallocate resources<br />
<br />
<br />
<br />
<code><nowiki>PROCESS_EXITHANDLER(handler) </code></nowiki><br />
<br />
Specifies an action when a process exits.<br />
NOTE: This declaration must come immediately before the PROCESS_BEGIN() macro.<br />
<br />
* handler: The action to be performed.<br />
<br />
<br />
<br />
<code><nowiki>PROCESS_BEGIN() </code></nowiki><br />
<br />
This macro defines the beginning of a process, and must always appear in a PROCESS_THREAD() definition.This macro initiates PT_BEGIN(), which is declared at the starting point of a protothread. All C statements above the PT_BEGIN() invokation will be executed each time the protothread is scheduled.<br />
<br />
<br />
<br />
<code><nowiki>broadcast_close(struct broadcast_conn *) </code></nowiki><br />
<br />
This function closes a broadcast connection that has previously been opened with broadcast_open().This function typically is called as an exit handler.<br />
<br />
*broadcast_conn : This is same as the variable from boradcast_recv().<br />
<br />
<br />
<br />
<code><nowiki> PROCESS_END() </code></nowiki><br />
<br />
This macro defines the end of a process. It must appear in a PROCESS_THREAD() definition and must always be included. The process exits when the PROCESS_END() macro is reached. This macro initiates PT_END().It must always be used together with a matching PT_BEGIN() macro.<br />
<br />
<br />
<br />
<code><nowiki> broadcast_open(struct broadcast_conn *, uint16_t ,const struct broadcast_callbacks *) </code></nowiki><br />
<br />
Sets up an identified best-effort broadcast connection. The caller will allocate the memory for the struct broadcast_conn, usually by declaring it as a static variable. The struct broadcast_callbacks pointer points to a structure containing a pointer to a function that will be called when a packet arrives on the channel. This function opens a connection of type abc_conn and sets the callbacks to structure passed. Also, this points to channel_set_attributes() function.<br />
<br />
*broadcast_conn : A pointer to a struct broadcast_conn<br />
*uint16_t: The channel on which the connection will operate<br />
*broadcast_callbacks : A struct broadcast_callbacks with function pointers to functions that will be called when a packet has been received<br />
<br />
<br />
<br />
<code><nowiki> etimer_set(struct etimer *, clock_time_t) </code></nowiki><br />
<br />
This function is used to set an event timer for a time sometime in the future. When the event timer expires, the event PROCESS_EVENT_TIMER will be posted to the process that called the etimer_set() function.<br />
<br />
*etimer : A pointer to the event timer<br />
*clock_time_t : The interval before the timer expires.<br />
<br />
<br />
<br />
<code><nowiki> static struct broadcast_conn </code></nowiki><br />
<br />
The broadcast module sends a packet to all local area neighbors with a header that identifies the sender. It also adds a single-hop address as a packet attribute to out going packets. <br />
broadcast_conn structure consists of two structures<br />
*abc_conn struct: the abc(Anonymous best effort local area Broadcast) module sends packets to all local area neighbors. It uses one channel.<br />
*broadcast_callbacks struct: this is called when a packet has been received by the broadcast module. The struct broadcast_callbacks pointer is used in broadcast_open to point to a function that will be called when a packet arrives on the channel.<br />
<br />
<br />
<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=141Broadcast Example2014-02-07T19:41:35Z<p>Nikhil: /* Macros and Structures */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn: ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky: ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1: ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2: ===<br />
<br />
Next, we will edit the data in the example-broadcast.c packet. In order to do this, you will need to open the packet within your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type: <br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
into terminal. This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3: ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4: ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5: ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. In the code, a process called example_braodcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
==== Macros and Structures ====<br />
<br />
<code><nowiki> PROCESS(name,strname) </nowiki></code><br />
<br />
The process has two names: the variable of the process structure, which is used by the C program, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
<code><nowiki> AUTOSTART_PROCESS(struct process &) </nowiki></code><br />
<br />
The AUTOSTART_PROCESSES definition specifices what processes to start when the module is loaded. We can pass more than one process.<br />
<br />
*&name: Refernce to process name<br />
<br />
<code><nowiki> broadcast_recv(struct broadcast_conn *, const rimeaddr_t *) </nowiki></code><br />
<br />
This function is called whenever a broadcast message is received. Reads the message, address of the sender and displays them.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to recv and sent functions.<br />
<br />
*rimeaddr_t *: This is union which has a array u8[RIMEADDR_SIZE] of type character.<br />
<br />
<code><nowiki> PROCESS_THREAD(name, process_event_t, process_data_t) </nowiki></code><br />
<br />
A process in Contiki consists of a single reference to "protothread".This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. A process always start with the PROCESS_BEGIN() and end with the PROCESS_END() function.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=140Broadcast Example2014-02-07T19:17:23Z<p>Nikhil: /* Macros and Structures */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn: ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky: ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1: ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2: ===<br />
<br />
Next, we will edit the data in the example-broadcast.c packet. In order to do this, you will need to open the packet within your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type: <br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
into terminal. This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3: ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4: ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5: ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. In the code, a process called example_braodcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
==== Macros and Structures ====<br />
<br />
<code><nowiki> PROCESS(name,strname) </nowiki></code><br />
<br />
The process has two names: the variable of the process structure, which is used by the C program, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
<code><nowiki> AUTOSTART_PROCESS(struct process &) </nowiki></code><br />
<br />
The AUTOSTART_PROCESSES definition specifices what processes to start when the module is loaded. We can pass more than one process.<br />
<br />
*&name: Refernce to process name<br />
<br />
<code><nowiki> broadcast_recv(struct broadcast_conn *, const rimeaddr_t *) </nowiki></code><br />
<br />
This function is called whenever a broadcast message is received. Reads the message, address of the sender and displays them.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to functions recv and sent functions.<br />
<br />
*rimeaddr_t *: This is union which has a array u8[RIMEADDR_SIZE] of type character.<br />
<br />
<code><nowiki> PROCESS_THREAD(name, process_event_t, process_data_t) </nowiki></code><br />
<br />
A process in Contiki consists of a single reference to "protothread".This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. A process always start with the PROCESS_BEGIN() and end with the PROCESS_END() function.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=139Broadcast Example2014-02-07T18:56:32Z<p>Nikhil: /* Macros and Structures */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn: ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky: ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1: ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2: ===<br />
<br />
Next, we will edit the data in the example-broadcast.c packet. In order to do this, you will need to open the packet within your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type: <br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
into terminal. This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3: ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4: ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5: ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. In the code, a process called example_braodcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
==== Macros and Structures ====<br />
<br />
<code><nowiki> PROCESS(name,strname) </nowiki></code><br />
<br />
The process has two names: the variable of the process structure, which is used by the C program, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
<code><nowiki> AUTOSTART_PROCESS(struct process &) </nowiki></code><br />
<br />
The AUTOSTART_PROCESSES definition specifices what processes to start when the module is loaded. We can pass more than one process.<br />
<br />
*&name: Refernce to process name<br />
<br />
<code><nowiki> broadcast_recv(struct broadcast_conn *, const rimeaddr_t *) </nowiki></code><br />
<br />
This function is called whenever a broadcast message is received. Reads the message, address of the sender and displays them.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to functions recv and sent functions.<br />
<br />
*rimeaddr_t *: This is union which has a array u8[RIMEADDR_SIZE] of type character.<br />
<br />
<code><nowiki> PROCESS_THREAD(name, process_event_t, process_data_t) </nowiki></code><br />
<br />
A process in Contiki consists of a single reference to "protothread".This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. A process always start with the PROCESS_BEGIN() and end with the PROCESS_END() function.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=138Broadcast Example2014-02-07T18:54:29Z<p>Nikhil: /* Macros and Structures */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn: ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky: ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1: ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2: ===<br />
<br />
Next, we will edit the data in the example-broadcast.c packet. In order to do this, you will need to open the packet within your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type: <br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
into terminal. This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3: ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4: ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5: ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. In the code, a process called example_braodcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
==== Macros and Structures ====<br />
<br />
<code><nowiki> PROCESS(name,strname) </nowiki></code><br />
<br />
The process has two names: the variable of the process structure, which is used by the C program, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
'''AUTOSTART_PROCESS(struct process &)'''<br />
<br />
The AUTOSTART_PROCESSES definition specifices what processes to start when the module is loaded. We can pass more than one process.<br />
<br />
*&name: Refernce to process name<br />
<br />
'''broadcast_recv(struct broadcast_conn *, const rimeaddr_t *)'''<br />
<br />
This function is called whenever a broadcast message is received. Reads the message, address of the sender and displays them.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to functions recv and sent functions.<br />
<br />
*rimeaddr_t *: This is union which has a array u8[RIMEADDR_SIZE] of type character.<br />
<br />
'''PROCESS_THREAD(name, process_event_t, process_data_t)'''<br />
<br />
A process in Contiki consists of a single reference to "protothread".This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. A process always start with the PROCESS_BEGIN() and end with the PROCESS_END() function.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=137Broadcast Example2014-02-07T18:53:06Z<p>Nikhil: /* Macros and Structures */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn: ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky: ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1: ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2: ===<br />
<br />
Next, we will edit the data in the example-broadcast.c packet. In order to do this, you will need to open the packet within your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type: <br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
into terminal. This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3: ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4: ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5: ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. In the code, a process called example_braodcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
==== Macros and Structures ====<br />
<br />
<code><nowiki>PROCESS(name,strname) </code></nowiki><br />
<br />
The process has two names: the variable of the process structure, which is used by the C program, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
'''AUTOSTART_PROCESS(struct process &)'''<br />
<br />
The AUTOSTART_PROCESSES definition specifices what processes to start when the module is loaded. We can pass more than one process.<br />
<br />
*&name: Refernce to process name<br />
<br />
'''broadcast_recv(struct broadcast_conn *, const rimeaddr_t *)'''<br />
<br />
This function is called whenever a broadcast message is received. Reads the message, address of the sender and displays them.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to functions recv and sent functions.<br />
<br />
*rimeaddr_t *: This is union which has a array u8[RIMEADDR_SIZE] of type character.<br />
<br />
'''PROCESS_THREAD(name, process_event_t, process_data_t)'''<br />
<br />
A process in Contiki consists of a single reference to "protothread".This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. A process always start with the PROCESS_BEGIN() and end with the PROCESS_END() function.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=136Broadcast Example2014-02-07T18:52:23Z<p>Nikhil: /* Macros and Structures */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn: ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky: ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1: ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2: ===<br />
<br />
Next, we will edit the data in the example-broadcast.c packet. In order to do this, you will need to open the packet within your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type: <br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
into terminal. This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3: ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4: ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5: ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. In the code, a process called example_braodcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
==== Macros and Structures ====<br />
<code><nowiki>PROCESS(name,strname) </code></nowiki><br />
<br />
The process has two names: the variable of the process structure, which is used by the C program, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
'''AUTOSTART_PROCESS(struct process &)'''<br />
<br />
The AUTOSTART_PROCESSES definition specifices what processes to start when the module is loaded. We can pass more than one process.<br />
<br />
*&name: Refernce to process name<br />
<br />
'''broadcast_recv(struct broadcast_conn *, const rimeaddr_t *)'''<br />
<br />
This function is called whenever a broadcast message is received. Reads the message, address of the sender and displays them.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to functions recv and sent functions.<br />
<br />
*rimeaddr_t *: This is union which has a array u8[RIMEADDR_SIZE] of type character.<br />
<br />
'''PROCESS_THREAD(name, process_event_t, process_data_t)'''<br />
<br />
A process in Contiki consists of a single reference to "protothread".This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. A process always start with the PROCESS_BEGIN() and end with the PROCESS_END() function.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhilhttp://anrg.usc.edu/contiki/index.php?title=Broadcast_Example&diff=135Broadcast Example2014-02-07T18:51:03Z<p>Nikhil: /* Understanding the code */</p>
<hr />
<div>[[Contiki_tutorials | Back to Contiki Tutorials]]<br />
<br />
__TOC__<br />
<br />
=== Introduction ===<br />
<br />
This exercise is an introduction into programming the [http://www.capsil.org/capsilwiki/index.php/TELOSB/TMote_Sky Tmote Sky]. It uses the Rime stack to communicate with other Contiki nodes over the radio. You will run the broadcast example already found in the Contiki core folders to send your name from a base station via broadcast packets. The exercise sends broadcast packets at random intervals between 20 and 40 seconds such that all Tmote Sky boards nearby will receive the packet. <br />
<br />
=== You will learn: ===<br />
<br />
* How to program the Broadcast communication found within the Rime stack<br />
* How to encapsulate data in packets<br />
<br />
=== Connect the Tmote Sky: ===<br />
Before continuing with the tutorial, plug a Tmote Sky into the computer’s USB port. The LEDs on the skymote will start blinking to indicate that the mote is connected.<br />
<br />
=== Step 1: ===<br />
<br />
The first step is to change directories. At this point you should be familiar with the Contiki 2.7 core folders. The Rime stack is located within the examples folder, so type <br />
<br />
<code><nowiki> cd contiki-2.7/examples/rime </nowiki></code><br />
<br />
directly into terminal. Once inside the rime folder, use the ls command to view all the files within it. We will be working specifically with example-broadcast.c.<br />
<br />
=== Step 2: ===<br />
<br />
Next, we will edit the data in the example-broadcast.c packet. In order to do this, you will need to open the packet within your text editor. In this exercise, we use gedit, but feel free to use whichever text editor you are most comfortable with. Type: <br />
<br />
<code><nowiki> gedit example-broadcast.c </nowiki></code><br />
<br />
into terminal. This will open your text editor, allowing you to edit the code inside the broadcast packet. <br />
<br />
=== Step 3: ===<br />
<br />
To edit the actual data that your Tmote Sky will broadcast, scroll down to the line that contains <br />
<br />
<code><nowiki> packetbuf_copyfrom(“Hello”, 6) </nowiki></code><br />
<br />
Change the character string to your name. Then change the number of characters accordingly. Remember to account for the null zero associated with character strings in C when changing the number of characters. The line should now read<br />
<br />
<code><nowiki> packetbuf_copyfrom(“yourName”, 9) </nowiki></code><br />
<br />
Save and exit the text editor. <br />
<br />
=== Step 4: ===<br />
<br />
In the next step we configure Tmote Sky. Connect your mote to your computer via USB. If you are using VirtualBox or Parallels, assure that the mote actually connects to your virtual machine (the LEDs on the mote will blink when connected to the laptop). <br />
<br />
Then in terminal, type <br />
<br />
<code><nowiki> sudo chown:user /dev/ttyUSB0 </nowiki></code><br />
<br />
You will be prompted to enter the password for your user account. If you are running Instant Contiki, the password is user. The sudo [http://en.wikipedia.org/wiki/Chown chown] command is used to change the owner of the file in Unix-systems.<br />
<br />
Note: If you manually installed Contiki, you must type in your username instead of user. Then, when prompted to do so, enter your password. <br />
<br />
=== Step 5: ===<br />
<br />
In this step, we will compile and upload the Broadcast packet to your Tmote Sky. In the terminal window, type<br />
<br />
<code><nowiki> make TARGET=sky example-broadcast.upload login </nowiki></code><br />
<br />
You will notice that instead of typing example-broadcast.sky, we use example-broadcast.upload. The .upload tells Contiki to install the binary onto the attached mote. Additionally, we add login to use the terminal to communicate with the mote over the serial interface referred to by /dev/ttyUSB0. Note that login can be specified later in another call to make, i.e. make login.<br />
<br />
This should compile and upload the example-broadcast.c packet such that your mote will be able to broadcast your name. After the compilation is complete, the text broadcast message sent... should appear in terminal every 2 to 4 seconds, indicating that your name (which we added within the text editor) is being broadcast to all other motes in the area.<br />
<br />
<br />
<br />
'''Congratulations! You have successfully programmed the Tmote Sky! '''<br />
<br />
'''Note:''' If at any point, you would like to exit the compilation/broadcast stage, press CTRL-C<br />
<br />
'''Tip:''' You can save the TARGET by typing make TARGET=sky savetarget into terminal to avoid compiling the target repeatedly.<br />
<br />
=== Understanding the code ===<br />
The purpose of this example is to test the broadcast layer in Rime. In the code, a process called example_braodcast_process is kicked off by AUTOSTART_PROCESSES.<br />
<br />
=== Macros and Structures ===<br />
'''PROCESS(name,strname)''' <br />
<br />
The process has two names: the variable of the process structure, which is used by the C program, and a human readable string name, which is used when debugging.<br />
<br />
*name: The variable name of the process structure.<br />
*strname: The string representation of the process name.<br />
<br />
'''AUTOSTART_PROCESS(struct process &)'''<br />
<br />
The AUTOSTART_PROCESSES definition specifices what processes to start when the module is loaded. We can pass more than one process.<br />
<br />
*&name: Refernce to process name<br />
<br />
'''broadcast_recv(struct broadcast_conn *, const rimeaddr_t *)'''<br />
<br />
This function is called whenever a broadcast message is received. Reads the message, address of the sender and displays them.<br />
<br />
*broadcast_conn *: This structure which has 2 structures : abc_conn, broadcast_callbacks *. The abc_conn is basic type of connection over which the broadcast connection is developed. And, the broadcast_callbacks point to functions recv and sent functions.<br />
<br />
*rimeaddr_t *: This is union which has a array u8[RIMEADDR_SIZE] of type character.<br />
<br />
'''PROCESS_THREAD(name, process_event_t, process_data_t)'''<br />
<br />
A process in Contiki consists of a single reference to "protothread".This function is used to define the protothread of a process. The process is called whenever an event occurs in the system. A process always start with the PROCESS_BEGIN() and end with the PROCESS_END() function.<br />
<br />
*name: The variable name of the process structure.<br />
*process_event_t: The variable of type character.If this variable is same as PROCESS_EVENT_EXIT then PROCESS_EXITHANDLER is invoked.<br />
<br />
[[Contiki_tutorials | Back to Contiki Tutorials]]</div>Nikhil