Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009....

108
Mote 애플리케이션 개발 "Development of Mote"

Transcript of Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009....

Page 1: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

Mote 애플리케이션 개발

"Development of Mote"

Page 2: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

Ⅰ NesC1. Introduction

1.1 개략적 설명 ··················································· 1

1.2 NesC의 탄생배경 ·············································· 1

1.3 응용 분야 ······················································· 1

2. Interfaces2.1 개 념 ····························································· 1

2.2 Interface instance ············································ 1

2.3 command와 event ············································ 2

2.4 Interface type ················································· 2

2.5 Interface의 양방향성 ········································· 2

2.6 Interface function ············································ 2

3. Component3.1 Configuration ··················································· 3

3.2 Module ··························································· 5

4. Concurrency in nesC4.1 Task ······························································ 5

4.2 Event ····························································· 8

4.3 Race condition ················································· 8

4.4 Atomic statements ············································ 12

5. Appendix5.1 nesC Compiler ················································· 14

5.2 nesC에서 사용하는 용어 ···································· 15

6. Component 분석

6.1 Micaz ADC ······················································ 16

6.2 Timer ····························································· 23

6.3 GENERICCOMM ················································ 36

6.4 Micaz Surge Routing ········································· 45

목 차

Page 3: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

6.5 Event Library ··················································· 64

6.6 MultihopRouter & MultihopEngineM Component ······ 83

Ⅱ TinyOS Operation1. TinyOS 란?

1.1 디렉토리 구조 ················································· 93

1.2 TinyOS Layers ················································· 94

1.3 TinyOS Component Hierarchy ····························· 95

2. TinyOS 설치하기

2.1 TinyOS 다운받기 ·············································· 95

2.2 설치과정 ························································· 96

2.3 What's Being Installed? ····································· 97

3. TinyOS Application 시작하기

3.1 application 설치하기 ·········································· 98

3.2 Blink Application ··············································· 99

3.3 CntToLedsAndRfm & RfmToLeds ························· 100

3.4 OscilloscopeRF ················································· 100

3.5 PC Application ················································· 101

3.6 Surge ····························································· 104

Page 4: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 1 -

Ⅰ. NesC

1. Introduction

1.1. 개략적 설명

nesC는 TinyOS의 프로그래밍 언어로event-based execution model의

component 기반 언어이다. nesC의 component는 state를내부에 가지

고 있고 잘 정의된 interface를 통해 interact 한다는 점에서

object와 유사하다. 그러나 component들과 component 사이의

interaction이 컴파일 시점에 정해진다는 점에서 큰 차이가 있다.

1.2. NesC의 탄생배경

nesC는 network embedded system C의 약자로 component 기반 C dialect이다. nesC는 TinyOS의 structuring concepts과 execution model을 구체화

하기 위해 고안된 Cprogramming language의 확장판으로 ‘NES-see'라 발음된다. nesC의

component는 state를 캡슐화하고, functionality를 갖는 state를 연결

한다는 점에서 object와 유사하다. nesC의 가장 큰 차이는 naming scope에 있다. Global namespace에서 함수나 변수를 참조하는 C++ 이나 Java의 object와는 달리 nesC의 component는 local namespace만을 이용한다. 즉 component는 component를 구현하는 함수를 선언

하는 것과 더불어 component가 call하는 함수를 선언해야만 한다는

것을 말한다.

1.3. 응용 분야

nesC는 구조적 개념과 TinyOS 실행 모델을 구체화하기 위해 디자인

된 C의 확장입니다. ToinyOS는 제한된 자원을 가진 sensor network node를 위해 디자인 된 event-driven operating system입니다.

2. Interfaces

2.1. 개념

NesC에서 interface는 양방향성을 가지고 제공자(provider)와 사용자

(user)가 되는 컴포넌트를 연결하는 포트의 역할을 수행한다. interface는 command와 event 타입의 함수로 정의된다.

2.2. Interface instancecomponent specification에서 특정한 interface type이다. instance name, 역할(provider나 user), interface type 그리고 선택적으로

Page 5: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 2 -

interface parameter를 가진다. parameter가 없는 interface instance는 simple interface instance이고, parameter가 있는 interface instance는 parameterised interface instance이다.

2.3. command와 event2.3.1. interface의 선언형

command와 event는 interface의 선언형으로 다음 표에서 간단

히 설명하고 있다.command : command로 정의된 함수는 현 컴포넌트의 module 부분

에 구현된 함수로서, 현 컴포넌트를 사용하는 상위 컴포

넌트에서 ‘call'명령을 통해 호출된다.

event : event로 정의된 함수는 현 컴포넌트를 사용하는 상위 컴포

넌트에 구현되어야 하는 함수로서, 특정 인터럽트나 조건이

만족되었을 경우, 현 컴포넌트가 어떤 정보를 상위 컴포넌

트에게 전달할 때 사용한다.

그림 Ⅰ-2-1 event와

command 타입 인터페이스

2.4. Interface type두 component(provider와 user)사이에 상호작용을 특성화한다. 이러한

specification은 command와 event의 집합을 형성한다. 각각의 interface type은 구분된 이름을 가진다.

2.5. Interface의 양방향성

interface의 provider는 command를 수행하고 interface user는 event를 수

행한다.

2.6. Interface function전체 application의 wiring 구문에 의해 특성화 됨으로서, component의

Page 6: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 3 -

command와 event의 행동을 나타내는 선언만 존재하는 함수

3. Component

TinyOS에서는 앞에서 설명한 인터페이스를 통해 다른 컴포넌트들 간의

연결 및 서로간의 함수 호출이 가능하다. 하나의 컴포넌트는 자신이 사

용할 하위 컴포넌트들을 선언하고, 그들 간의 연결을 정의하는

configuration 파일과 자신의 구현 내용을 기술하고 있는 module 파일로

이루어 진다. 일반적으로 <컴포넌트 이름>.nc 또는 <컴포넌트 이

름>M.nc 같은 이름은 module을 위해 사용되는 파일 이름이다.

3.1. Configurationconfiguration은 다른 컴포넌트와의 연결에 대한 내용을 정의하고 있

는데, 연결에 사용할 컴포넌트를 나열하고 그들 간의 연결을 기술하

는 방법은 다음과 같다.

configuration C { provides interface X;}implementation { components C1, C2;

X = C1.X; X = C2.X;}

C는 선언된 컴포넌트 이름을 의미하며, 그 안에는 현 컴포넌트에서

제공하는 인터페이스(X)가 선언되어 있다. 그리고 그 밑에 기술된

implementation 부분에는 현 컴포넌트에서 사용할 하부 컴포넌트들

(C1, C2)이 차례대로 선언된다. 그 아래의 문장들은 컴포넌트들 간의

연결을 기술하고 있다. 컴포넌트들 사이의 연결을 wiring이라고 한

다.

nesC는 관계가 있는 함수들을 모으기 위하여 "interfaces"라는 것이

있다. Component의 "specifications"는 거의 항상 "interfaces" 의 term으로 되어 있다. 예를 들면 power 의 management 및 configration 문제에서, application은 종종 센서 값을 읽기위하여 센서를 켜거나

packet을 읽어 들이기 위하여 radio stack을 켜기 위하여 system abstraction과 service를 "start" 및 "stop'할 수 있어야 할 필요가 있

다. "StdControl" interface는 이러한 것을 표현하는 보편적인 방법으

로 다음과 같이 interface를 나타낼 수 있다.

interface StdControl { command error_t start(); command error_t stop();}

Page 7: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 4 -

켜거나 끌 수 있는 abstraction 또는 service를 표현하는 component는 "StdControl"을 provides하고 다른 것을 켜거나 끌 필요가 있는

component "StdControl"을 "uses"한다. 이것은 대체로 계층적 관계를

갖는다. 예를들면 routing layer는 data link layer를 start하고 idel channel을 감지하여 start 및 stop할 필요가 있다.

module RoutingLayerC { provides interface StdControl; uses interface StdControl as SubControl;}

module PacketLayerC { provides interface StdControl;}

provider와 user를 함께 연결하는 것을 "wiring"이라고 부른다. 예를

들면 RoutingLayerC의 코드는 SubControl.start() 및 SubControl.stop()을 call하는 함수를 갖는다. SubControl이 provider에 "wiring"되지 않

는다면 이 함수는 undefined symbols이며, 어떤 실제의 코드와도 관

계가 없다. 그러나 SubControl이 PacketLayerC의 StdControl에 wiring된다면 RoutingLayerC가 SubControl.start()를 호출할 때 PacketLayerC의 StdControl.start()를 invoke한다. 이것은 RoutingLayerC.SubControl.start의 참조는 PacketLayerC.StdControl.start의 정의를 가리킴을 의미한다. 두

component RoutingLayerC 및 PacketLayerC는 완전히 decouple되어

있고 wiring될 때만 bound 된다.

3.1.1. 와이어링의 종류

와이어링은 ‘->’, ‘<-’, ‘=’ 세 가지의 기호로 나타낸다.interface1=interface2

2개의 interface가 같음을 의미하며 양쪽 interface가 서로 같은 제

공자이거나 사용자인 경우와 한쪽은 제공자이고 다른 쪽은 사용자

인 경우를 의미한다.

interface1->interface2

interface의 구성 함수가 링크되어 있음을 의미한다. 즉, interface1

에서 사용한 함수가 interface2에 구현되어 있음을 나타낸다.

interface1<-interface2

interface2->interface1과 동일한 의미이다.

Page 8: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 5 -

module FilterMagC {

provides interface StdControl;

provides interface Read<uint16_t>;

uses interface Timer<TMilli>;

uses interface Read<uint16_t> as RawRead;

}

implementation {...}

module PeriodicReaderC {

provides interface StdControl;

uses interface Timer<TMilli>;

uses interface Read<uint16_t>;

}

3.2. Module3.2.1. Implementing the Module’s Specification

module은 C 코드로 된 component 특성화 작업을 수행한다.

module-implementation

implementation { translation-unit }

transltion-unit은 C 선언과 정의에 관한 목록이다.transltion-unit은 반드시 모듈상의 제공된 모든 command(event)를 수행해야 한다.(직접적으로 제공된 모든 command와 event, 제공된 interface상의 모든 command와 사용된 interface상에서

의 모든 event) module은 그것의 command를 호출할 수 있고

그것의 event에 대한 신호를 보낼 수 있다.

3.2.2. Calling Commands and Signaling Events단일 command는

> call command-name(...)

에 의해 호출되어 지고, 단일 event는> signal event-name

에 의해 신호가 보내어 진다. 예를 들어 SendMsg 타입의

interface send를 사용하는 module에서는

> call Send.send(1, sizeof(Message), &msg1)로 표현된다.

4. Concurrency in nesC

4.1. TasknesC에서는 command에서 event를 signal하는 것은 좋지 않은 방법

에 해당한다. 왜냐하면 그러한 implementation은 call loop을 발생할

수 있기 때문이다. 다음의 코드를 살펴보자.

Page 9: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 6 -

implementation {

uint16_t filterVal = 0;

uint16_t lastVal = 0;

command error_t StdControl.start() {

return call Timer.startPeriodic(10);

}

command error_t StdControl.stop() {

return call Timer.stop();

}

event void Timer.fired() {

call Read.read();

}

event void Read.readDone(error_t err, uint16_t val) {

if (err == SUCCESS) {

lastVal = val;filterVal *= 9;

filterVal /= 10;

filterVal += lastVal / 10;

}

}

command error_t Read.read() {

signal Read.readDone(SUCCESS, filterVal);

//command 내부에서 event Read.readDone()을 signal 함

}

이와 같이 command 내부에서의 event의 signaling은 stack과 관련하

여 심각한 문제를 발생시킬 수 있다. 예를 들어 FasSamplerC가 high sampling rate로 많은 횟수의 센서 값을 sample하려고 할 때 다음과

같이 implementation을 하게 된다.

event void Read.readDone(error_t err, uint16_t val) { buffer[index] = val;index++; if (index < BUFFER_SIZE) { call Read.read(); }}

이러한 경우 read와 readDone 사이에 긴 call lop가 형성될 수 있다. complier가 이러한 것을 optimize할 수 없을 때에는 stack이 증가하게

된다. 메모리가 큰 PC같은 경우에는 그다지 중요하지 않을 수 있지

만, MSP430과 같이 극히 제한된 RAM의 양과 하드웨어 emory protection이 없는 경우 이와 같은 stack의 증가는 data memory를 훼

손하고 프로그램이 깨어지는 결과를 초래할 수 있다. 따라서

command 내부에서 event를 signal하는 것은 좋은 프로그래밍 방법

이 아니다.

이러한 문제를 피하기 위하여 deffered procedure call인 task를이용

Page 10: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 7 -

module FilterMagC {

provides interface StdControl;

provides interface Read<uint16_t>;

uses interface Timer<TMilli>;

uses interface Read<uint16_t> as RawRead;

}

module PeriodicReaderC {

provides interface StdControl;

uses interface Timer<TMilli>;

uses interface Read<uint16_t>;

}

implementation {

uint16_t filterVal = 0; uint16_t lastVal = 0;

task void readDoneTask();

command error_t StdControl.start() {

return call Timer.startPeriodic(10);

}

command error_t StdControl.stop() {

return call Timer.stop();

}

event void Timer.fired() {

call RawRead.read();

}

event void RawRead.readDone(error_t err, uint16_t val) {

if (err == SUCCESS) { lastVal = val; filterVal *= 9; filterVal /= 10; filterVal += lastVal / 10;

한다. Module은 task를 scheduler에게 post할 수 있다. 그러면 어떤

시간이 지난 시점에서 scheduler는 task를 수행하게 된다. task가 즉

시 call되지 않기 때문에 return 값은 없다. 그리고 task는 component의 naming scope안에서 수행되기 때문에 어떤 parameter도 취하지

않는다. function과 같이 task는 미리 선언할 수 있으며 그 형식은 다

음과 같다.

task void readDoneTask();

Component는 다음과 같은 keyword로 scheduler에게 task를 post한다.

post readDoneTask();

stack의 문제가 발생하지 않도록 고치면 처음의 예제 코드를 다음과

같이 표현할 수 있다.

Page 11: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 8 -

}

}

command error_t Read.read() {

post readDoneTask();

return SUCCESS;

}

task void readDoneTask() { signal Read.readDone(SUCCESS, filterVal); }}

4.2. EventTinyOS에서는 실행된 모든 task가 끝난 후, 큐가 비어 있을 경우에는

다른 task가 생성되기 전까지 CPU의 전원을 최소화하여 CPU의 소모

에너지를 낮춘다. task는 다른 task에 의해 선점되지 않지만, event에

의해서는 선점된다. event는 특정 하드웨어 인터럽트나 특정 조건을

만족했을 경우 호출되는 프로세스로서 다른 task보다 먼저 실행되는

특징이 있다. 일반적으로 인터럽트에 의해 어떤 event가 발생되면

연결된 컴포넌트들에 따라 연속적으로 상위 컴포넌트들의 특정 함수

가 호출되며, 동시에 그에 따른 여러 처리 함수들이 task 형태로 만

들어져 FIFO 큐에 저장된다.

4.3. Race conditionTask는 non-preemptive이기 때문에 어떤 시점에서 오직 하나의 task만이 수행된다. 즉 다른 task가 수행되고 있는 중에는 이 task를

preemption하고서 다른 task를 수행할 수 없다는 것이고 따라서 수

행 중인 task가 종료될 때까지 다른 task들은 모두 기다려야만 한다. 이 모든 것은 scheduler가 알아서 하기 때문에 user는 신경을 쓸 필

요가 없다. 즉 task가 수행 중인데 다른 task가 post되면 먼저 수행

중인 task가 종료되면 그 다음 task가 자동으로 수행이 된다. 그리고

서로 간에 data가 훼손되거나 간섭이 발생하지 않게 scheduler가 처

리를 하게 된다. 그러나 task의 경우 non-preemtive이기 때문에 매우

긴 시간을 점유하는 것은 바람직하지 못하다. 따라서 task는 가능한

한 짧게 구성을 해야 한다. 그러나 task가 길어질 수 밖에 없는 경우

도 있다. 그러한 경우는 task를 하나로 구성하지 말고 여러개의 task로 나누어 구성을 하는 것이 바람직하다.

Post operation은 error_t를 return 한다. task가 task queue에 있지

않으면 SUCCESS를 return하고 task queue에 있으면 (posted but not run yet) FAIL을 return 한다.일반적으로 component가 task를 여러번

run할 필요가 있다면 그 task를 repost해야 한다. 다음의 두 가지 코

드는 비슷하나 다른 면이 있다.

Page 12: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 9 -

command error_t Read.read() {

return post readDoneTask();

}

위의 코드는 split-phase call의 one-to-one binding에 해당한다. 반

면 many-to-one 여러번 호출 해야할 때는 다음과 같이 구현한다.

command error_t Read.read() {

post readDoneTask();

return SUCCESS;

}

Task는 소프트웨어 component가 하드웨어의 split-phase behavior를

emulation할 수 있도록 해준다. 그러나 시스템에서 preemption을 관

리하기 위한 mechanism을 제공하는 task 보다 더 유용한 방법이 있

다. task 끼리는 서로에 대하여 atomically(mom preemptive하게)수행

이되기 때문에 task에서 수행되는 code는 비교적 간단해 질 수 있다.왜냐하면 갑작스럽게다른 것이 수행되거나 data가 변경되는 위험성

이 없기 때문이다. 그러나 인터럽트는 다른 것이 수행되거나 data가

중간에 변경될 수 있다. 인터럽트는 형재의 실행을 중지하고

preemptive하게 수행을 시작한다. nesC와 tinyos에서 function은 task context 외부로부터 preemtive하게 수행이 가능한데 이것은 async keyword를 수반한다. 인터럽트는 task에 대해 비동기적으로 수행이

일어난다. nesC의 규칙은 async function call인 command와 async function signal인 event는 async이다. 즉 비동기가 아닌 command나

event나 call 할 수 없다. 비동기가 아닌 function은 동기가 되어 있고

간단히 sync라고 부른다. 기본적으로 command와 event는 sync이다. 만약 sync가 아니면 async keyword를 붙여준다. Interface 정의는

command와 event가 sync인지 async인지를 규정한다. 예를 들면 다

음과 같은 interface는 모두 synchronous(동기)이다.

interface Send { command error_t send(message_t* msg, uint8_t len); event void sendDone(message_t* msg, error_t error); command error_t cancel(message_t* msg); command void* getPayload(message_t* msg); command uint8_t maxPayloadLength(message_t* msg);}

Page 13: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 10 -

반면 다음은 모두 asynchronous(비동기)이다.

Interface Leds { async command void led0On(); async command void led0Off(); async command void led0Toggle(); async command void led1On(); async command void led1Off(); async command void led1Toggle(); async command void led2On(); async command void led2Off(); async command void led2Toggle(); async command uint8_t get(); async command void set(uint8_t val);}

모든 인터럽트 핸들러는 async이고 인터럽트 핸들러는 어떤 sync function도 call graph (make tmote docs 하면 생기는 graph)를 포함

할 수 없다. 인터럽트 핸들러가 sync function을 실행할 수 있는 유

일한 방법은 task를 post하는 것이다. task post는 async operation이

지만 수행하는 task는 sync이다.

예를들어, UART의 top에서의 packet layer를 생각해보자. UART가

byte를 받을 때, 인터럽트가 발생하고 인터럽트 핸들러에서 소프트웨

어는 data register의 값을 읽고 buffer에 저장한다. packet의 마지막

byte가 수신되었을 때, 소프트웨어는 packet reception을 signal할 필

요가 있다. 그러나 Receiver interface의 receive event는 sync이다. 그

래서 마지막 byte의 인터럽트 핸들러에서 component는 packet reception을 알리기 위해 task를 post한다.

그러면 여기서 질문이 생긴다. 앞서의 설명에 따르면 task는 latency가 발생하는데 왜 task를 사용하는 것일까? 왜 모든 것을 async하게

만들지 않을까?

그 질문에 대한 답은 바로 race condition 때문이다. race condition이

란 둘 이상의 process가 동일한 resource를 사용하기 위하여 다투는

것으로 어느 프로세스가 먼저 resource를 점유하느냐에 따라 그 결

과가 달라지게 된다.

이러한 race condition은 preemptive execution이 가능할 때 현재 수

행 중인 state를 바꿈으로써 state의 consistency가 보장할 수 없게

될 때 발생한다.

다음의 경우를 생각해보면 toggle command는 state bit를 toggle하고

값을 return 한다.

Page 14: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 11 -

async command bool toggle() { if (state == 0) { state = 1; return 1; } if (state == 1) { state = 0; return 0; }}

state=0에서 시작해서 다음과 같은 수행을 생각해 보자.component가 toggle을 call하면 component는 state의 return 값이 1이 될 것이라고 예측한다. 그러나 인터럽트가 종료 전에 발생하고

인터럽트에 의해 toggle이 call되면 component가 예측하는 것과 달리

return 값은 0이 되어 버린다. 이러한 문제는 single statement에 대

해 인터럽트가 발생할 때 더 심각해 질 수 있다. 예를 들면 32bit number가 둘 이상의 instruction으로 쓰거나 읽어야 할 때 두

instruction 사이에 인터럽트가 발생할 수 있다. 그러면 읽는 숫자의

값이 다른 새로운 숫자와 섞여서 전혀 예측하지 못한 결과를 얻게

된다.

이 예제의 경우는 state variable에서 발생하는 것으로 data race라

부른다. tinyos에서 packet을 abstraction하는 AMStandard 코드의 일

부를 살펴보자. state variable은 component가 busy인지 아닌지를 나

타낸다.

command result_t SendMsg.send ... { if (!state) { state = TRUE;// send a packet return SUCCESS; } return FAIL;}

이 command가 async라면 "if (!state)" 조건과 "state=TRUE" 사이에

또 다른 component가 끼어들어 packet을 역시 전송하려고 시도할

수 있다. 이 두번째 call 입장에서는 state가 false라고 판단하기 때문

에 state를 true로 바꾸고 packet을 보내고 SUCCESS를 return 한다. 그러나 그후 첫번째 call은 state를 true로 바꾸고 전송을 시작하고

SUCCESS를 return 한다. 그러면 두 packet 중의 하나만 전송이 완료

되어 error가 발생한다. 이러한 경우 에러의 원인을 찾기가 매우 어

렵다. 이로 인해 모든 call sequence 상에서 logical error가 발생할

수 있다. 따라서 프로그램을 할 때 다음과 같은 사실을 염두에 두어

야 한다.

Page 15: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 12 -

Code는 가능한 synchronous를 유지하게 한다. Code는 timing이 매우

중요하거나 timing이 중요한 다른 component 등에 의해 사용이 는

경우에만 async 해야 한다.

인터럽트 문제는 프로그램들이preemption이 될 수 없는 code의 단편

을 수행할 수 있도록 하는 방법을 필요로 한다. nesC는 atmoic statements를 통해 이러한 기능을 제공한다.

4.4. Atomic statementsnesC의 키워드 중에 atomic 이라는 것이 있다. Atomic은 사전적으로

물질을 구성하는 최소 단위인 "원자의"라는 뜻을 가지고 있다. 이 말

에서 유추할 수 있듯이 더 이상 쪼갤 수 없는 프로세스를 생각해보

면, atomic이라는 것은 오직 하나의 프로세스만이 수행되는 것을 말

한다. 아래의 예는 atomic section에 의해 값의 증가가 일어나는 것

이다.

command bool increment() { atomic { a++; b = a + 1; }}

atomic block은 block 내의 variable들이 atomically 읽어지거나 써지

는 것을 보장한다. 그러나 이 말이 atomic block이 preemption이 될

수 없는 것을 의미하지는 않는다. Atomic block이 preemption 되지

않는다면 효율이 떨어질 것이다. Atomic block이 있다 할지라도 두

code segment 사이에 같은 variable이 없다면 서로 preemption을 시

킬 수 있다. 그래야 효율이 더 좋아진다. 다음의 예를 보면

async command bool a() { atomic { a++; b = a + 1; }}async command bool c() { atomic { c++; d = c + 1; }}

이 예제에서 보면 a()와 c()는 atomic으로 되어 있지만 c()는 a()를

preemption 시킬 수 있다. 왜냐하면 a()를 preemption 시켜도 c()의

variable은 그 결과에 영향을 받지 않기 때문이다. 그러나 c()는 c()

Page 16: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 13 -

자체를 preemption 시킬 수 없다.

nesC는 atomic scetion을 제공하기 위해 variable들이 적절히

protection이 되었는지 check하고 protection이 되지 않았다면

warning을 발생시킨다. 예를 들면 앞 예제에서 b와 c가 atomic section으로 구성되지 않는다면, nesC는자신을preemption시킬 가능성

때문에 warning을 발생시킨다. Variable이 atomic section에 의해

protection되는 규칙은 variable이 async function으로부터 access가

되면 variable은 반드시 protection 되어야만 한다는 것이다. nesC는

flow에 senstive하다. 즉 atomic block을 포함하지 않는 function이 있

고 이 function이 atomic block 내에서 call이 된다면 컴파일러는

warning을 발생하지 않는다. 그러나 atomic block 내에서 call 되지

않는다면 불필요하게 많은 nested atomic block이 생길 수 있다. 대

체로 atomic block은 인터럽트를 disable하는 것과 같은 종류의 수행

에만 관계가 된다. 그래서 불필요한 atomic block은 CPU cycle을 낭

비하게 된다. 그래서 nesC는 redundant한 atomic block을 제거한다.

Atomic block의 가장 기본적인 이용은 component에서의 state transition이다. 대체로 state transition은 두 부분으로 구성되고 현재

의 state와 call에 의해 결정이 된다. 첫 부분은 새로운 state로 바꾸

는 것이고 두 번째는 어떤 action을 취하는 것이다. 즉 다음과 같다.

if (!state) { state = TRUE;// send a packet return SUCCESS;}else { return FAIL;}

만약 state가 async function에 의해 사용되어 진다면, state transition을 atomic하게 만들 필요가 있다. 그러나 전체 block을 atomic section으로 구성하고 싶지 않다면 다음과 같이 구성을 할 수 있다.

uint8_t oldState;

atomic { oldState = state; state = TRUE;}if (!oldState) {//send a packet return SUCCESS;}else { return FAIL;}

Page 17: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 14 -

NAME

ncc - nesC compiler for TinyOS

SYNOPSIS

ncc [-target=pc|mica|mica2|mica2dot|...] [-tosdir=dir] [-print-tosdir] [-print-platforms] [-nostdinc] [-board=micasb|basicsb|micawb|...] [-docdir=dir] [-topdir=dir] [-graphviz=y|n] [-fnesc-nido-tosnodes=n] [-fnesc-cfile=file] [-fnesc-no-inline [-Wnesc-xxx] [any gcc option] files...

DESCRIPTION

ncc is an extension to gcc that knows how to compile nesC applications. If invoked on regular C files, it behaves exactly like gcc. When invoked on a nesC component or interface (.nc extension) file it compiles and links (except if the usual -c, -S, -E or -fsyntax-only options are used) that component with the other files specified on the command line.

The additional options recognized by ncc over gcc are:

-target=X

specify the target architecture for this compilation. If pc is specified, the compilation uses the tossim environment and produces a locally executable file. The default target is mica, the possible targets are set by the TinyOS distribution (see the tos/platforms directory). A platform that is not in the TinyOS distribution can be used if its directory is specified with an explicit -I directive (the platform name is taken from the directory's name, platform directories are recognised by the presence of a .platform file).

-tosdir=dir

specify the location of TinyOS. This location can also be specified with the `TOSDIR' environment variable. If the variable and the option are both given, ncc uses the value specified with the option. If neither the environment variable or option are specified, ncc uses its compiled-in TinyOS directory.

-print-tosdir

print the TinyOS directory to be used and exit, taking into account the -tosdir option and `TOSDIR' environment variable. No compilation occurs when -print-tosdir is used.

-print-platforms

print the valid TinyOS platforms, including those made available by explicit -I directives (see -target discussion above).

-nostdinc

do not automatically include the TinyOS directories in the search path. See the discussion of search paths below for more details.

-board=Y

specify one (or more) sensor boards. This effects the search path and preprocessor symbols. The set of boards is set by

5. Appendix

5.1. nesC Compiler

Page 18: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 15 -

the TinyOS distribution (see the tos/sensorboards directory). As with targets, a sensorboard directory can be made available via an explicit -I directive (sensorboard directories are recognised by the presence of a .sensor file).

-docdir=dir

generate documentation for the compiled component in directory dir.

-topdir=dir

specify directory paths that should be stripped from the source file names when generating "package names" for the documentation files. The directory above TOSDIR is automatically added, so this option is only needed for directories outside the main TinyOS distribution.

-graphviz=y|n

explicitly enable or disable the use of the graphviz tool in the generated documentation. Without this option, graphviz is enabled iff the `dot' program is found in the current path. Use of graphviz requires `dot'. The documentation generation tool checks the version of `dot', and enables client-side image maps, if supported.

-fnesc-tossim-tosnodes=n

specify the maximum number of nodes that can be simulated in the tossim environment.

-fnesc-cfile=file

specify a file in which to save the C code generated when compiling a component. Note: if you specify two components on the command line, then the C code from the second one will overwrite the C code from the first.

5.2. nesC에서 사용하는 용어

application하나 이상의 컴포넌트로 구성되며, 실제 센서 노드에서 실행

가능한 하난의 프로그램

component

component - NesC를 구성하는 기본 블록으로, 컴포넌트를 정

의하는 configuration과 module로 구분된다.

interface - 2개의 컴포넌트 사이를 연결하기 위해 정의된 포트

로, 컴포넌트는 여러 개의 interface를 사용할 수 있으며, 이

interface를 이용하여 command, message가 처리된다. 두 컴

포넌트 사이의 연결 통로(인터페이스)를 연결하는 것을 wiring

이라고 한다.

configuration - 하나의 새로운 컴포넌트를 정의하고, 이곳에서

사용할 다른 하부 컴포넌트들을 선언한다. 그리고 이들 간의

연결(wiring)을 어떻게 정의할 것인가에 대해 기술한다.

module - 새로운 컴포넌트의 동작 acl 다른 컴포넌트들과의

연동을 실제로 구현하는 곳이다.

Page 19: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 16 -

6. Component 분석

6.1. Micaz ADCADC (Analog Digital Converter)는 말그대로 아날로그의 신호를 디지

털의 값으로 바꾸어준다는 것으로, mote의 sensorboard에서 sensing된 값을 app에서 얻고자 할때 쓰인다.

이번 분석에서 hardware는 micaz를 썼고, "tos/system/ADCM.nc tos/system/ADCC.nc", "tos/platform/avrmote/HPLADCC.nc" 파일을 분

석하였다.

먼저, ADCC.nc 파일을 살펴보자.

보는 바와 같이 ADCControl와, ADC[uint8_t port]를 제공하고 있다. ADCM과 HPLADCC components를 사용하고 있고, ADCM에 ADC와

ADCContol에 대한 구현이 있다. 마지막 줄은 ADCM의 HPLADC는

HPLADCC의 HPLADC에 연결되어 있다는 것을 보인다.

ADCC.nc는 비교적 간단하게 구현되어 있으므로, 소스를 보면 이해가

가리라 생각한다.

다음은 ADCM.nc를 보자.module 부분을 먼저 보겠다.

Page 20: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 17 -

module부분은 해당 module에서 어떤 interface를 사용하고 제공하는

지를 나타내고 있다.보면 알기에 설명은 생략하겠다.

ADCM의 implementation 부분을 보기 전에 ADCM이 HPLADCC에 연

결되어 있기에 간략하게 HPLADCC.nc에서 제공하는 command와

event를 보겠다.

제공하는 command는 다음과 같다.

그림 Ⅱ-6-1 Command 종류

이름만 봐도 어떤 기능을 하는 command인지 대충은 알 수 있을 것

이다.

Page 21: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 18 -

event는 다음과 같다.

그림 Ⅱ-6-2 Event 종류

다음으로 ADCM에서 제공하는 command들을 살펴본다. command들은 다음과 같다.

하나하나의 자세한 의미는 잠시후에 살펴보겠다. 일단은 이런 것들

이 있음을 알아놓는다.

이제 구체적으로 ADCM 소스를 살펴보겠다.

module에서 사용할 변수들을 선언한 부분이다.

다음에 볼 부분은 init()부분이다. ADC를 사용하고자 한다면 처음으

로 수행되어져야 하는 것이다. 쉽게 말해서 Sensor로부터 값을 가져

오기 위해 pin들을 초기화한다고 보면 되겠다.

소스를 보자.

여기서는 단순히 사용하는 변수들을 ‘0’으로 초기화시키고, HPLADC.init()을 호출하고 있다. 그렇다면 HPLADC.init()을 따라가보자.

Page 22: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 19 -

다음과 같이 init_portmap()을 수행하고, 그 밑의 부분은 avr을 콘트

롤하기 위한 부분인데 hardware에 관해선 그렇게 아는바가 없어서, 알아보니, 첫 번째 outp()은 ADCSR이라는 레지스터에 0x04의 값을

넣는 의미이고, cbi(ADCSR, ADSC)의 의미는 ADCSR의 ADSC값을

clear하라는 의미이고, sbi는 반대로 set하라는 의미. 그래서 결국엔

센싱된 값이 들어오면 interrupt를 하기위해 ADIF,ADIE 값을 set 해

주고, 아직 application이 값을 가지고 오지를 원치 않기 때문에

ADSC(ADC start conversion), ADEN(ADC enable)을 clear한다. init_portmap()에서 하는 일을 보자.

PORTMAPSIZE만큼 루프를 돌면서 port mapping table을 초기화 하고

있다.

ADCControl.setSampleRate(uint8_t rate)를 알아보겠다. ADCM.nc의

소스를 보면,

다음과 같이 되어있다. 단순히 HPLADC.setSamplingRate 를 호출하고

있다는걸 볼수 있다. 그렇다면 HPLADCC.nc 파일의 setSamplingRate를 보자.

Page 23: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 20 -

ADC의 clock을 결정하기 위한 레지스터가 ADCSR인데 초기화 시켜줄

때 0x04의 값을 넣어준것을 볼 수 있었다. 모르겠다면 ADC.init()부분을 다시 보기 바란다. 위에 있다. 그 값을 변경시키기 위한

command이다. rate를 넘겨받아서 ADCSR에 rate를 넣어준다.

init_portmap()부분에서 실제의 포트를 가상의 포트테이블로 mapping 시키는 것을 볼 수 있었다. 이 부분을 remap하기 위한 command가

있는데, 바로 bindPort이다.

실제의 포트와 가상의 포트가 어떻게 정의되어 있는지 보기위해선, platform폴더아래에 있는 해당폴더의 hardward.h 파일과 sensorboard폴더 아래의 해당센서보드의 폴더의 sensorboard.h 파일을 살펴보기

바란다.

위의 setSamplingRate와 마찬가지로 단순히 HPLADC.bindPort를 호출

하고 있다.

가상의 포트 테이블의 port 번호를 넘겨받아서, adcPort라는 실제

port번호를 mapping 해주고 있다.

다음은 ADCM.nc에서 중요한 event인 HPLADC.dataReady를 알아 보

겠다. dataReady event는 ADC로부터 센싱된 데이터의 값이 준비가

되면 signal되어진다. 그렇다면 우선 어떻게 signal 되어지는지

HPLADCC.nc의 ADC.dateReady의 소스를 살펴보자.

Page 24: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 21 -

위와 같이 TOSH_SIGNAL(SIG_ADC) 신호가 오면 __nesc_enable_interrupt()를 이용하여 interrupt를 주고 ADC.dataReady를 signal 하는 것을 볼 수 있

다. 위와 같은 일이 일어나면 다음으로 ADCM.nc의 ADC.dataReady가 수행

된다.

ADC.dateReady의 소스를 보자.

HPLADC.sampleStop()이라는 cammand가 나오는데 그것은 단순히 지

금 conversion 할수 없기에 sampling을 멈추라는 것이다. 자세한 소

스는 HPLADCC.nc에 나와있다. 참고하기바란다. sampleStop외에도

sampleAgain()이라는 command도 있는 데 그것은 이름그대로 센싱정

보를 다시 한번 sampling 하는것이다. 마찬가지로 HPLADCC.nc에 나

와있다.

그리고 가장 중요한 부분을 보겠다. application이 센싱된 값을 가져

오기를 원하면 getData command를 호출해야하는 데, 소스는 다음과

같다.

Page 25: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 22 -

단순히 startGet이라는 함수를 호출하고 있는 것을 볼 수 있다. startGet의 두 개의 인자는 말그대로 한번 convert, 계속적인 convert를 말하고 port는 어떤 물리적 port에서 센싱된 값을 가져올것인지를

말한다.

startGet을 보자.

state와 port를 넘겨 받으면 해당 port에서 센싱된 값을 받을 준비를

하는데 그 부분이

이 부분이다. samplePort를 호출하고 있는 데, 살펴보자.

Page 26: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 23 -

위와 같이 해당 포트로 샘플링을 하기 위해서 ADCSR의 ADEN과

ADSC를 set하고 있다. 두 비트에 관한 설명은 위에 init 부분에 있다.

6.2. TIMER

그림 Ⅱ-6-3 전체 시스템의 구성 및 소스

전체 구성을 간단히 설명하자면 여기서 configuration은 2개가 있다. TimerC와 ClockC 두가지가 있는데 TimerC는 StdControl, Timer 라는

인터페이스를 제공한다. 물론 그 구현은 TimerM이라는 module에 있

다(wiring에 의해). TimerM에서는 Leds, Clock, PowerManagement 라

는 interface를 제공하는데(여기서 제공한다는 것은 TimerM에 각각의

인터페이스의 실제 구현이 들어있다는 것이다.) 각각은 차례대로

NoLeds, ClockC, HPLPowerManagement로 wiring된다. NoLeds와

HPLPowerManagement는 그 자체가 끝이며 ClockC는 configuration으

로 ClockC가 제공하는 Clock, StdControl의 실제 구현은 HPLClock에

있다. 정리하자면 Timer(일반적 의미)는 Clock(일반적 의미)을 사용하

여 시간을 측정하기 때문에 Clock 이라는 interface를 사용하고

Timer(일반적 의미)를 동작시키는데 중간중간에 Power조절을 하므로

HPLPowerManagement라는 컴포넌트를 사용한다.분석의 방법은 main이 되는 TimerM을 분석하기 위해 TimerM에서

사용하는 NoLeds, ClockC, HPLPowerManagement라는 컴포넌트를 먼

저 알아본다. 그러면 각각의 컴포넌트가 제공하는 함수들의 역할을

알 수 있고 이를 TimerM이 어떻게 이용하여 Timer를 작동시키는가

알아볼 것이다.

분석의 순서는 다음과 같다.NoLeds -> ClockC -> HPLPowerManagement -> TimerM

Page 27: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 24 -

6.2.1. NoLeds

module NoLeds {

provides interface Leds;

}

implementation

{

async command result_t Leds.init() {

return SUCCESS;

}

async command result_t Leds.redOn() {

return SUCCESS;

}

async command result_t Leds.redOff() {

return SUCCESS;

}

async command result_t Leds.redToggle() {

return SUCCESS;

}

async command result_t Leds.greenOn() {

return SUCCESS;

}

async command result_t Leds.greenOff() {

return SUCCESS;

}

async command result_t Leds.greenToggle() {

return SUCCESS;

}

async command result_t Leds.yellowOn() {

return SUCCESS;

}

async command result_t Leds.yellowOff() {

return SUCCESS;

}

async command result_t Leds.yellowToggle() {

return SUCCESS;

}

async command uint8_t Leds.get() {

return 0;

}

async command result_t Leds.set(uint8_t value) {

return SUCCESS;

}

}

각 command들을 보면 SUCCESS만 return할 뿐 아무런 작업을

하지 않는다. 또한 TimerM에서도 NoLeds를 wiring 시켜놓은

Leds라는 인터페이스를 사용하지 않는다. 그런점으로 볼 때 이

컴포넌트는 그냥 사용자가 필요한 일을 할 때에 사용하라고

연결시켜 놓은 컴포넌트로 보인다.(Leds라는 interface는 mote의 led를 On/Off시키는 동작을 정의해놓은 interface이다.)

Page 28: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 25 -

6.2.2. ClockC

Commands

• result_t setRate (char interval, char scale) Set the clock rate.

• void setInterval(uint8_t value) Set clock interval

Parameters: value - New clock interval

Returns: none

• void setNextInterval(uint8_t value) Set clock interval at next clock interrupt time

Parameters: value - New clock interval

Returns: none

• uint8_t getInterval(void) Get clock interval

Returns:current clock interval

• uint8_t getScale(void) Get clock scale

Returns:current clock scale level

• void setNextScale(uint8_t scale) Set clock scale at next clock

interrupt time Parameters: scale - New clock scale Returns: none

• result_t setIntervalAndScale(uint8_t interval, uint8_t scale) Set both clock interval and scale

Parameters: interval - New clock interval

scale - New clock scale

Returns: SUCCESS or FAILED

• uint8_t readCounter(void) Read HW clock counter

• void setCounter(uint8_t n) Set HW clock counter to a specified value

Parameters: n - Value to write to TCNT0

Returns: None

• void intDisable(void) Disable Clock interrupt

• void intEnable(void) Enable Clock interrupt

Events

• result_t fire(void) An event sent when the clock goes off.

이 컴포넌트에서 TimerM에서 사용하는 command, event만 본다면 다음과 같다.setInterval - 다음 Clock interrupt time부터 clock interval을 바꿔준다.getInterval - Clock interval을 가져온다.readCounter - H/W clock counter를 읽어온다.setRate- clock Rate를 설정한다.fire - clock이 종료되었을 때 호출되는 이벤트

여기서 setRate의 방식을 잠깐 살펴보자.• result_t setRate (char interval, char scale)

Page 29: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 26 -

먼저 인자로 들어가는 interval은 tick 수를 나타내며 scale은 platform의 영향을 받는 값으로 다음의 리스트와 같다.

☞ Clock scale

0 - off

1 - 32768 ticks/second

2 - 4096 ticks/second

3 - 1024 ticks/second

4 - 512 ticks/second

5 - 256 ticks/second

6 - 128 ticks/second

7 - 32 ticks/second

이 command는 몇초마다 clock을 발생시킬건지를 결정한다. 예를 들어 setRate(160,7)이라고 했을 때

160ticks/32ticks/second160/32=5∴매 5초마다 clock가 발생한다.

6.2.3. HPLPowerManagement

Provided Interfaces

• PowerManagement

• result_t Enable(void)

• result_t Disable(void)

Variables

• bool disabled = TRUE

Function Index

• uint8_t getPowerLevel(void)

• task void doAdjustment(void)

• async command uint8_t PowerManagement.adjustPower (void)

• command result_t Enable(void)

• command result_t Disable(void)

이 컴포넌트와 wiring된 interface는 adjustPower라는 command하나만을 정의한다. 또한 TimerM에서 사용하는 command도 adjustPower하나이다. 이 command는 hardware의 현재 상태에 따라 Power를 조절한다. 어떠한 상태가 있고 어떻게 조절하는지는 hardware에 대한 지식이 필요한 부분이라서 넘어가고 대충 코드로 짐작을 해 보면 POWER_SAVE, EXT_STANDBY등의 상태가 있는 것으로 보인다.

Page 30: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 27 -

6.2.4. TimerMTimerC가 제공해 주는 interface가 2개가 있는데 StdControl은

기본 init, start, stop가 정의되어 있는 interface이고, interface Timer를 알아본다.

Commands

• result_t start (char type, uint32_t interval) Start the timer.

• result_t stop (void) Stop the timer, preventing it from firing again.

Events

• result_t fired(void) The signal generated by the timer when it fires.

기본 커멘드로 start와 stop, 이벤트로 fired가 있다. start는 타이머를 시작하는 것이고 stop는 타이머를 종료하는 커맨드이다. 타이머를 시작하고 셋팅된 시간이 경과하면 fired이벤트가 발생해 원하는 작업을 코딩할 수 있다.

이제 TimerM을 보자

Provided Interfaces

• Timer

• StdControl

그림 Ⅱ-6-4

먼제 제공되어 지는 interface는 기본 interface인 StdControl과 위에서 살펴본 바 있는 Timer interface 2가지 이다. 이제 두가지 interface들의 command들과 event들의 구현이 있는 TimerM을 보자.

Required Interfaces

• Leds

• Clock

• PowerManagement

Provided Interfaces

• Timer

• StdControl

Page 31: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 28 -

implementation {

uint32_t mState; // each bit represent a timer state

uint8_t setIntervalFlag;

uint8_t mScale, mInterval;

int8_t queue_head;

int8_t queue_tail;

uint8_t queue_size;

uint8_t queue[NUM_TIMERS];

volatile uint16_t interval_outstanding;

struct timer_s {

uint8_t type; // one-short or repeat timer

int32_t ticks; // clock ticks for a repeat timer

int32_t ticksLeft; // ticks left before the timer expires

} mTimerList[NUM_TIMERS];

Variables

• uint32_t mState

• uint8_t setIntervalFlag

• uint8_t mScale

• uint8_t mInterval

• int8_t queue_head

• int8_t queue_tail

• uint8_t queue_size

• uint8_t queue[NUM_TIMERS]

• volatile uint16_t interval_outstanding

• struct TimerM.timer_s { uint8_t type; int32_t ticks; int32_t ticksLeft; } mTimerList[NUM_TIMERS]

Function Index

• command result_t StdControl.init (void)

• command result_t StdControl.start (void)

• command result_t StdControl.stop (void)

• command result_t Timer.start (uint8_t id, char type, uint32_t interval)

• static void adjustInterval(void)

• command result_t Timer.stop (uint8_t id)

• event result_t Timer.fired (uint8_t id)

• void enqueue(uint8_t value)

• uint8_t dequeue(void)

• task void signalOneTimer(void)

• task void HandleFire(void)

• async event result_t Clock.fire (void)

Page 32: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 29 -

enum {

maxTimerInterval = 230

};

type는 타이머를 계속 반복할 지 한번만 실행할 지 결정하는 속성이다.tos interfaces Timer.h₩ ₩ 를 보면 값들이 정의되어 있다. 그리고 maxTimerInterval을 230으로 줬는데 그 이유는 이 하드웨어가 8bit timer를 사용하므로 0~255까지를 셈할 수 있기 때문에 적당선으로 230을 잡아준 것 같다.

#ifndef NTIMERS

#if NESC >= 110

#define NTIMERS uniqueCount("Timer")

#else

#define NTIMERS 12

#endif

#endif

enum {

TIMER_REPEAT = 0,

TIMER_ONE_SHOT = 1,

NUM_TIMERS = NTIMERS

};

tos₩interfaces₩Timer.h

command result_t StdControl.init() {

mState=0;

setIntervalFlag = 0;

queue_head = queue_tail = -1;

queue_size = 0;

mScale = 7;

mInterval = maxTimerInterval;

return call Clock.setRate(mInterval, mScale) ;

}

프로그램을 실행할 때 timer state, queue, timer, clock들을 초기화 한다. queue는 다들 안다고 생각하고 나머지 변수들을 보면

mState - 개개의 타이머가 실행중인지를 나타내는 플래그 변수

이다.

setIntervalFlag - Timer가 시작할 때 0, 종료(fire)될때 1로 셋팅

한다.

mScale - hardware가 초당 몇 번의 tick을 발생시키는지 결정

mInterval - 타이머를 몇 번의 tick발생마다 종료시킬것인지 결정

Page 33: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 30 -

command result_t StdControl.start() {

return SUCCESS;

}

별다른 동작없이 그냥 시작하면 SUCCESS를 반환한다.

command result_t StdControl.stop() {

mState=0;

mInterval = maxTimerInterval;

setIntervalFlag = 0;

return SUCCESS;

}

종료시 기본 변수들을 초기화 시켜주고 SUCCESS를 반환한다.

command result_t Timer.start[uint8_t id](char type, uint32_t interval) {

uint8_t diff;

if (id >= NUM_TIMERS) return FAIL;

if (type > TIMER_ONE_SHOT) return FAIL;

if ((type == TIMER_REPEAT) && interval <= 2) return FAIL;

mTimerList[id].ticks = interval ;

mTimerList[id].type = type;

atomic {

diff = call Clock.readCounter();

interval += diff;

mTimerList[id].ticksLeft = interval;

mState|=(0x1L<<id);

if (interval < mInterval) {

mInterval=interval;

call Clock.setInterval(mInterval);

setIntervalFlag = 0;

call PowerManagement.adjustPower();

}

}

return SUCCESS;

}

TIMER_REPEAT일때 interval에 제한을 두는 이유는 하드웨어 clock이 비교값을 셋팅하는 동안에 증가될 수 있기 때문이란다.(이 사실이 왜 문제가 되는지 정확한 이유는 아직 잘 모르겠다.)현재 clock counter값을 읽어와 interval에 더해준다. 그리고 그 결과값이 최대 interval값보다 작으면 그 값을 Clock의 interval로 설정하고 power를 조정한다. 중간에 timer의 identifyer인 id

Page 34: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 31 -

task void HandleFire() {

uint8_t i;

uint16_t int_out;

를 이용해 mState의 id번째 bit를 1로 셋팅해 주는 코드도 보인다.mTimerList는 다음의 구조체를 가지는 배열이다.

struct timer_s {

uint8_t type; // one-short or repeat timer

int32_t ticks; // clock ticks for a repeat timer

int32_t ticksLeft; // ticks left before the timer expires

} mTimerList[NUM_TIMERS];

여기서 type은 timer의 동작 방식(ONE-SHOT, REPEAT), ticks는 timer가 동작하는 clock tick, ticksLeft는 timer가 종료되기까지 남은 ticks를 의미한다.

async event result_t Clock.fire() {

atomic {

/* DCM: Once we've posted HandleFire(), don't post it

again until

* the original one is handled. This prevents the task

queue

* from getting flooded when mInterval is small. */

if (interval_outstanding == 0)

post HandleFire();

else

dbg(DBG_ERROR, "Don't post handle fire, we're

not ready₩n");

/* DCM: Keep track of the interval since the last

interrupt */

interval_outstanding += call Clock.getInterval() + 1;

}

return SUCCESS;

}

}

Clock이 발생할 때마다 실행되는 이벤트 인데 여기서는 interval_outstanding이라는 flag를 둬서 HandleFire가 한번에 하나만 post될 수 있도록 하는데 그 이유는 interval이 작을 경우 queue를 관리하기 위함이다. 만약 post된 뒤 수행되고 있는 task가 없다면 HandleFire를 post하고, post되었으나 아직 수행은 되지 않은 task가 있다면(interval_outstanding가 0이 아닐 경우) Clock의 interval을 읽어와 1을 더한 값을 interval_outstanding에 더해준다.

Page 35: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 32 -

setIntervalFlag = 1;

/* DCM: read the number of ticks elapsed since the

last firing

* was handled. */

atomic {

int_out = interval_outstanding;

interval_outstanding = 0;

}

if (mState) {

for (i=0;i<NUM_TIMERS;i++) {

if (mState&(0x1L<<i)) {

mTimerList[i].ticksLeft -= int_out;

if (mTimerList[i].ticksLeft<=2) {

/* DCM: only update the timer structure

if the

* signalOneTimer() task was able to

be posted. */

if (post signalOneTimer()) {

if (mTimerList[i].type==TIMER_REPEAT) {

mTimerList[i].ticksLeft += mTimerList

[i].ticks;

} else {// one shot timer

mState &=~(0x1L<<i);

}

enqueue(i);

}

else {

dbg(DBG_ERROR, "TimerM: Have to

wait another time interval.₩n");

/* DCM: wait another interval in

hopes that

* the task queue will clear out. */

mTimerList[i].ticksLeft = mInterval;

}

}

}

}

}

/* DCM: don't bother adjusting the interval if another

interrupt

* is hot on our tail. */

atomic int_out = interval_outstanding;

if (int_out == 0)

adjustInterval();

}

Page 36: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 33 -

마지막 firing 발생을 처리한 이후의 시간이 interval_outstanding에 있다 그 때문에 이 값을 int_out에 넣고 interval_outstanding에 0을 넣어준다. 0을 넣어줘야 다음의 Clock.fired()이벤트를 처리할 수 있기 때문에 이러한 작업을 한다.첫 번째 조건에서 어느 timer에서 발생한 것인지를 판단하며 다음 조건에서는 signalOneTimer()가 post될 수 있는지를 체크한다. 만약 post될 수 없는 상황이라면 ticksLeft를 mInterval로 셋팅함으로써 다음 firering을 기다린다.signalOneTimer()의 post가 성공하면 timer의 type이 ONE SHOT인지 REPEAT인지를 판단하여 REPEAT이면 ticks를 다시 설정해 주고 one shot이면 mState의 해당 타이머 bit를 해제한다.queue에 i를 삽입하고 루프는 끝이 나며 이상의 과정중에 interval_outstanding이 생기지 않았으면 adjustInterval()을 호출한다.

task void signalOneTimer() {

uint8_t itimer = dequeue();

if (itimer < NUM_TIMERS)

signal Timer.fired[itimer]();

}

이 task는 HandleFire()에서 queue에 넣은 timer identifyer를 꺼내어 해당 timer의 fired event를 signal 시킨다.

void enqueue(uint8_t value) {

if (queue_tail == NUM_TIMERS - 1)

queue_tail = -1;

queue_tail++;

queue_size++;

queue[(uint8_t)queue_tail] = value;

}

uint8_t dequeue() {

if (queue_size == 0)

return NUM_TIMERS;

if (queue_head == NUM_TIMERS - 1)

queue_head = -1;

queue_head++;

queue_size--;

return queue[(uint8_t)queue_head];

}

enqueue(...)와 dequeue()는 일반 queue 연산과 같다

Page 37: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 34 -

default event result_t Timer.fired[uint8_t id]() {

return SUCCESS;

}

Timer.fired event는 SUCCESS를 반환한다 사용자는 이제 이 event를 wiring하여 필요한 작업을 코딩하면 된다.

static void adjustInterval() {

uint8_t i, val = maxTimerInterval;

if ( mState) {

for (i=0;i<NUM_TIMERS;i++) {

if ((mState&(0x1L<<i)) && (mTimerList[i].ticks

Left <val )) {

val = mTimerList[i].ticksLeft;

}

}

/* DCM: If the interval is set to be less than the

current

* counter value, the timer will count an extra 256 ticks

before

* hitting the interrupt. Thus, we check for this

condition

* and avoid it. */

/* PAL: This piece of code sets a maximum interrupt

rate

* that TimerM will request for continuous timers. TimerM

* will never request an interrupt less than 3ms from the

* current time; it therefore returns FAIL on continuous

* timers with an interval <= 2 (see Timer.start()). */

atomic {

i = call Clock.readCounter() + 3;

if (val < i) {

val = i;

}

mInterval = val;

call Clock.setInterval(mInterval);

setIntervalFlag = 0;

}

} else {

atomic {

mInterval=maxTimerInterval;

call Clock.setInterval(mInterval);

setIntervalFlag = 0;

}

}

call PowerManagement.adjustPower();

}

Page 38: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 35 -

동작중인 timer가 있다면 clock의 interval을 조정해 주고 없다면 interval을 최대값으로 셋팅한 뒤 power를 조정해 준다.

command result_t Timer.stop[uint8_t id]() {

if (id>=NUM_TIMERS) return FAIL;

if (mState&(0x1L<<id)) { // if the timer is running

atomic mState &= ~(0x1L<<id);

if (!mState) {

setIntervalFlag = 1;

}

return SUCCESS;

}

return FAIL; //timer not running

}

timer[id]가 동작중이라면 mState를 해제하고 setIntervalFlag를 1로 셋팅해 준다. 만약 timer[id]가 동작중이지 않다면 FAIL을 반환할 것이다.

이상으로 TimerC, TimerM분석을 마친다. ticksLeft값의 변경이라든가 setIntervalFlag 값의 변경 등 Timer의 동작에 관해 아직 이해가 부족한 부분이 있는데 이는 추후 update한다.

Page 39: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 36 -

implementation

{

// CRCPacket should be multiply instantiable. As it is, I have to use

// RadioCRCPacket for the radio, and UARTNoCRCPacket for the UART to

// avoid conflicting components of CRCPacket.

components AMStandard,

RadioCRCPacket as RadioPacket,

UARTFramedPacket as UARTPacket,

NoLeds as Leds,

TimerC, HPLPowerManagementM;

Control = AMStandard.Control;

SendMsg = AMStandard.SendMsg;

ReceiveMsg = AMStandard.ReceiveMsg;

sendDone = AMStandard.sendDone;

activity = AMStandard.activity;

6.3. GENERICCOMM

그림 Ⅱ-6-5 전체 시스템 구성 및 요소

GenericComm은 통신(Communication - UART, RADIO)을 하기위한

일반화된 컴포넌트이다. 다음은 기본 인터페이스이다.

☞ Interface

StdControl : 기본 컨트롤 인터페이스

SendMsg : 해당 매체(Radio or Uart)로 Message를 송신

ReceiveMsg : 해당 매체(Radio or Uart)로부터 Message를 수신

function

activity : AMStandard에 정의된 함수(lsatcount를 반환)

sendDone : AMStandard에 정의된 함수(Message 송신이 성공했

을 결우 발생되는 이벤트로 success를 반환한다.)

Page 40: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 37 -

AMStandard.TimerControl -> TimerC.StdControl;

AMStandard.ActivityTimer -> TimerC.Timer[unique("Timer")];

AMStandard.UARTControl -> UARTPacket.Control;

AMStandard.UARTSend -> UARTPacket.Send;

AMStandard.UARTReceive -> UARTPacket.Receive;

AMStandard.RadioControl -> RadioPacket.Control;

AMStandard.RadioSend -> RadioPacket.Send;

AMStandard.RadioReceive -> RadioPacket.Receive;

AMStandard.PowerManagement ->

HPLPowerManagementM.PowerManagement;

}

위의 wiring을 보면 StdControl, SendMsg, ReceiveMsg각각은 Timer,

UART, Radio의 Stdcontrol, SendMsg, ReceiveMsg로 wiring되고 모든

구현은 AMStandard에 있다는 것을 알 수 있다.

AMSTANDARD

그림 Ⅱ-6-6 AMStandard

이 컴포넌트는 UART 또는 RADIO로 패킷을 전송하는 컴포넌트이다.

위의 구성도는 오실로스코프 구성도의 일부분이다. 여기서 중점적으

로 알아볼 것은 AMStandard의 기본동작 분석이다. TimerC는 하나의

컴포넌트로 따로 분석이 되어있으므로 생략하겠다. 그러면

PacketSink와 UARTFramedPacket 컴포넌트가 남게 되는데 PacketSink

는 오실로스코프에서 Radio를 사용하지 않기 때문에 RadioCRCPacket

을사용하는 대신 아무런 작업을 하지 않는 컴포넌트로 구현만 해 놓

은 컴포넌트이다. 따라서 RADIO 통신은 RadioCRCPacket이라는 컴포

넌트를 따로 분석하면서 알아보겠다. UARTFramedPacket도 마찬가지

로 AMStandard의 분석을 마친 후 따로 분석한다.

Page 41: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 38 -

Require Interfaces

• result_t sendDone(void) • StdControl UARTControl • BareSendMsg UARTSend • ReceiveMsg UARTReceive • StdControl RadioControl • BareSendMsg RadioSend • ReceiveMsg RadioReceive • StdControl TimerControl • Timer ActivityTimer • PowerManagement

Provided Interfaces

• StdControl Control • SendMsg • ReceiveMsg • uint16_t activity(void)

Variables

• bool state • TOS_MsgPtr buffer • uint16_t lastCount • uint16_t counter

Function Index

• command bool Control.init (void) • command bool Control.start (void) • command bool Control.stop (void) • command uint16_t activity(void) • void dbgPacket(TOS_MsgPtr data) • result_t reportSendDone(TOS_MsgPtr msg, result_t success) • event result_t ActivityTimer.fired (void) • event result_t SendMsg.sendDone (uint8_t id, TOS_MsgPtr msg,

result_t success) • event result_t sendDone(void) • task void sendTask(void) • command result_t SendMsg.send (uint8_t id, uint16_t addr, uint8_t

length, TOS_MsgPtr data) • event result_t UARTSend.sendDone (TOS_MsgPtr msg, result_t

success) • event result_t RadioSend.sendDone (TOS_MsgPtr msg, result_t

success) • TOS_MsgPtr received(TOS_MsgPtr packet) • event TOS_MsgPtr ReceiveMsg.receive (uint8_t id, TOS_MsgPtr msg) • event TOS_MsgPtr UARTReceive.receive (TOS_MsgPtr packet) • event TOS_MsgPtr RadioReceive.receive (TOS_MsgPtr packet)

Page 42: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 39 -

이제 AMStandard의 각 함수들을 알아보자

command bool Control.init() {

result_t ok1, ok2;

call TimerControl.init();

ok1 = call UARTControl.init();

ok2 = call RadioControl.init();

state = FALSE;

lastCount = 0;

counter = 0;

dbg(DBG_BOOT, "AM Module initialized₩n");

return rcombine(ok1, ok2);

}

초기화 하는 부분이다. UARTControl과 RadioControl, 그리고

TimerControl을 초기화 한다. counte는 읽은 packet수를 의미하는 변

수이다. UARTControl과 RadioControl이 모두 정상적으로 초기화 되면

true를 반환한다.

command bool Control.start() {

result_t ok0 = call TimerControl.start();

result_t ok1 = call UARTControl.start();

result_t ok2 = call RadioControl.start();

result_t ok3 = call ActivityTimer.start(TIMER_REPEAT, 1000 * TIMER

_PRESCAL);

// HACK -- unset start here to work around possible lost calls to

// sendDone which seem to occur when using power management. SRM 4.4.03

state = FALSE;

call PowerManagement.adjustPower();

return rcombine4(ok0, ok1, ok2, ok3);

}

Timer, UART, Radio를 시작하고 Timer를 반복적으로 동작시킨다. 타

이머의 주기는 1000*TIMER_PRESCAL이고 TIMER_PRESCAL 속성은

위에서 정의되었다.power를 조정하고 실패한 동작이 없으면 rtue를

반환한다.

Page 43: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 40 -

command bool Control.stop() {

result_t ok1 = call UARTControl.stop();

result_t ok2 = call RadioControl.stop();

result_t ok3 = call ActivityTimer.stop();

// call TimerControl.stop();

call PowerManagement.adjustPower();

return rcombine3(ok1, ok2, ok3);

}

application이 종료될때 UART, Radio를 종료하고 작동하고 있는

Timer를 정지시킨다. Timer의 Control을 정지시키는 부분은 주석처리

되어 있는데 왜 주석처리해 뒀는지는 모르겠다. 모든 동작이 성공하

면 true를 반환한다.

command result_t SendMsg.send[uint8_t id](uint16_t addr, uint8_t

length, TOS_MsgPtr data) {

if (!state) {

state = TRUE;

if (length > DATA_LENGTH) {

dbg(DBG_AM, "AM: Send length too long: %i. Fail.₩n", (int)length);

state = FALSE;

return FAIL;

}

if (!(post sendTask())) {

dbg(DBG_AM, "AM: post sendTask failed.₩n");

state = FALSE;

return FAIL;

}

else {

buffer = data;

data->length = length;

data->addr = addr;

data->type = id;

buffer->group = TOS_AM_GROUP;

dbg(DBG_AM, "Sending message: %hx, %hhx₩n₩t", addr, id);

dbgPacket(data);

}

return SUCCESS;

}

return FAIL;

}

Page 44: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 41 -

이 코드를 보면 state의 쓰임을 알 수 있다. state는 현재 프로세스가

어떤 작업을(여기서는 message sending) 하고 있는지를 알려주는 변

수이다. 즉 message를 전송할 때에는 TRUE로 셋팅해서 다른 작업으

로부터 보호해 주고 끝나면 FALSE로 셋팅해 작업이 끝났음을 알려

준다. 이러한 과정을 거치는 이유는 buffer를 사용하기 때문인것 같

다. post를 사용하여 메시지를 전송하는데 UART로 전송할 것인지

Radio로 전송할 것인지는 sendTask()에서 addr을 보고 판단한다.

task void sendTask() {

result_t ok;

TOS_MsgPtr buf;

buf = buffer;

if (buf->addr == TOS_UART_ADDR)

ok = call UARTSend.send(buf);

else

ok = call RadioSend.send(buf);

if (ok == FAIL) // failed, signal completion immediately

reportSendDone(buffer, FAIL);

}

위의 if문장에서 UART로 전송할 것인지 Radio로 전송할 것인지를 판

단한다.

여기서 사용되는 TOS_MsgPtrd은 TOS_Msg의 포인터 구조체이다.

typedef struct TOS_Msg

{

/* The following fields are transmitted/received on the radio. */

uint16_t addr;

uint8_t type;

uint8_t group;

uint8_t length;

int8_t data[TOSH_DATA_LENGTH];

uint16_t crc;

/* The following fields are not actually transmitted or received

* on the radio! They are used for internal accounting only.

* The reason they are in this structure is that the AM interface

* requires them to be part of the TOS_Msg that is passed to

* send/receive operations.

*/

uint16_t strength;

uint8_t ack;

uint16_t time;

uint8_t sendSecurityMode;

uint8_t receiveSecurityMode;

} TOS_Msg;

Page 45: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 42 -

전송이 완료되면 다음의 이벤트가 각각 발생한다.

event result_t UARTSend.sendDone(TOS_MsgPtr msg, result_t success) {

return reportSendDone(msg, success);

}

event result_t RadioSend.sendDone(TOS_MsgPtr msg, result_t success) {

return reportSendDone(msg, success);

}

reportSendDone(msg, success)함수의 원형이다.

result_t reportSendDone(TOS_MsgPtr msg, result_t success) {

state = FALSE;

signal SendMsg.sendDone[msg->type](msg, success);

signal sendDone();

return SUCCESS;

}

state를 FALSE로 바꾸어 주고 다음의 두 개 함수를 signal하는데

default event result_t SendMsg.sendDone[uint8_t id](TOS_MsgPtr

msg, result_t success) {

return SUCCESS;

}

default event result_t sendDone() {

return SUCCESS;

}

둘다 특별한 작업없이 SUCCESS만 return한다.

이상으로 메시지 전송 작업은 완료된다.

이제 메시지를 수신할 때를 분석해 보자.

UART나 Radio로 메시지가 들어오면 다음의 이벤트들이 발생하여

received를 호출하게 된다.

event TOS_MsgPtr UARTReceive.receive(TOS_MsgPtr packet) {

// A serial cable is not a shared medium and does not need group-id

// filtering

packet->group = TOS_AM_GROUP;

return received(packet);

}

event TOS_MsgPtr RadioReceive.receive(TOS_MsgPtr packet) {

return received(packet);

}

Page 46: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 43 -

그럼 이제 received를 살펴보겠다.

TOS_MsgPtr received(TOS_MsgPtr packet) __attribute__ ((C, spontaneous)) {

uint16_t addr = TOS_LOCAL_ADDRESS;

counter++;

dbg(DBG_AM, "AM_address = %hx, %hhx; counter:%i₩n", packet

->addr, packet->type, (int)counter);

if (packet->crc == 1 && // Uncomment this line to check crcs

packet->group == TOS_AM_GROUP &&

(packet->addr == TOS_BCAST_ADDR ||

packet->addr == addr))

{

uint8_t type = packet->type;

TOS_MsgPtr tmp;

// Debugging output

dbg(DBG_AM, "Received message:₩n₩t");

dbgPacket(packet);

dbg(DBG_AM, "AM_type = %d₩n", type);

// dispatch message

tmp = signal ReceiveMsg.receive[type](packet);

if (tmp)

packet = tmp;

}

return packet;

}

__attribute__ 는 이 fuction에 속성을 지정해 주는 gcc의 문법인데

spontaneous는 다른 일반 function에서 이 function을 사용할 수 없

다는 것을 의미한다. 여기서 의문은 received는 UARTReceive.receive

와 RadioReceive.receive에서 호출을 했다는 것이다. 그게 가능한 이

유는 UARTReceive.receive와 RadioReceive.receive가 이벤트 핸들러에

의해 호출되는 것이기 때문에 프로그래머가 임의로 호출했다 하더라

도 그 안에서 호출되는 function이기 때문에 위의 특성과 맞아떨어진

다고 볼 수 있다.

nesC에서는 3가지 속성을 지원하는데 설명은 다음과 같다. C의 정확

한 의미는 잘 모르겠다.

Page 47: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 44 -

• C: This attribute is used for a C declaration or definition d at the

top-level of a module (it is ignored for all other declarations). It

specifies that d’'s should appear in the global C scope rather than

in the module’'s per-component-implementation scope. This allows

d to be used

(e.g., called if it is a function) from C code.

• spontaneous: This attribute can be used on any function f (in

modules or C code). It indicates that there are calls f that are not

visible in the source code. Typically, functions that are called

spontaneously are interrupt handlers, and the C main function.

Section 9 discusses how the nesC compiler uses the spontaneous

attribute during compilation.

• combine(fnname): This attribute specifies the combining function

for a type in a typedef declaration. The combining function

specifies how to combine the multiple results of a call to a

command or event which has “"fan-out”".

지금 이 function에서는 TOS_AM_GROUP일 때에만 ReceiveMsg.receive

이벤트를 발생시킨다. ReceiveMsg.receive는 다음과 같은데 받은 메시

지를 그대로 return할 뿐이다. 즉 프로그래]머가 어떤 작업을 하도록

재정의 해주지 않으면 받은 메시지를 그대로 return하는 default event

가 호출되는 것이다.

default event TOS_MsgPtr ReceiveMsg.receive[uint8_t id](TOS_MsgPtr

msg) {

return msg;

}

counter는 받은 메시지의 개수 정보를 유지하는데(패킷을 받을 때

마다 증가시킨다.) timer를 동작시켜서 fire될 때마다 lastcount에 저

장을 하고 count는 0으로 셋팅한다.

event result_t ActivityTimer.fired() {

lastCount = counter;

counter = 0;

return SUCCESS;

}

activity는 lastCount를 반환한다.

command uint16_t activity() {

return lastCount;

}

이상으로 AMStandard.h의 분석을 마치겠다.

Page 48: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 45 -

6.4. Micaz Surge Routing먼저 surge app는 multihop으로 동작을 하며 센서 노드가 센싱데이

터를 자신의 부모에게 전달하여 결국에는 sink node가 센싱된 데이

터를 수집하여 sink node에 연결된Uart(PC)로 전송한다.

그림 Ⅱ-6-7 SurgeApp 구조

<그림1>은 surge app의 구조를 나타낸다.

Main에서 시작되는 StdControl은 각 Component들에 연결되어있다. Main이 시작할 때

Main에 연결된Component의 init() 호출

그 후 start() 호출

그림에서 Main에 연결된 Component를 살펴보면TimerC, Photo,GenericCommPromiscuous, SurgeM, MultiHopRouter, Bcast, QueuedSend 이다.왜 이렇게 되는지 직접 소스를 보고 확인해보자. 여러분도 이 문서만 보지말고, 직접 꼭 해당 파일을 찾아가서 확인

해보기 바란다.

Page 49: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 46 -

그림 Ⅱ-6-8 Main 구조

tos/system 폴더에 있는 RealMain.nc 파일을 보자.

이제 알수 있을것이다. StdControl을 이용해 init()후에 start()를 호출

하고 있다. 이 부분은 모든App에 거의 공통적이므로 필히 알아두도록 하자. 이제 본격적으로 Routing 관련된 부분으로 들어가보겠다.

그림 Ⅱ-6-9 MultiHopRouter 구조

MultiHopRouter과 MultiHopEngineM은 점선으로 연결되어있는데, 이

Page 50: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 47 -

것을 (=)Equal 관계를 의미한다. 이것은 곧 실제로 MultiHopRouter에서는 하는 일이 없고 MultiHopEngineM에서 모든일을 담당한다는 뜻

이다. MultiHopEngineM에서는 MultiHopLEPSM에서 제공(provide)하는

function들을 사용해서 surge에서 보내는 SurgeMsg 관련된 일을 담

당하여 처리한다.

먼저 message의 구조를 알아보고 넘어가도록 하겠다. TinyOS에서는 radio를 통해 전송하는 데이터에 대해서 일반적인

message 형태를 제공하는데 이것을 Tos_Msg 라고 한다. 아래 그림에서 보듯이 Tos_Msg내에 Multihop_Msg가 packing 되어있

고, Multihop_Msg내에 Surge_Msg가 packing 되어있다.

그림 Ⅱ-6-10 Message 구조

Page 51: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 48 -

Surge Message 의 타입은surge.h 파일에서 보면 17인 것을 확인할

수 있다.

enum { AM_SURGEMSG = 17}

Multihop Message는multihop.h에 나와있다.

enum { AM_MULTIHOPMSG = 250}

Message에 대한 부분은 이정도로 마무리하기로 하고, 자세한것은 소

스를 보기 바란다.

이제 Surge.nc, SurgeM.nc, Surge.h 파일을 살펴볼 차례이다.SurgeM.nc를 보자.

Page 52: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 49 -

이렇게 되어있을것이다. Init()에서 initialize()를 호출하고 각종 변수를 초기화해준다. 그후 start()가 호출될 때 timer를 start해준다. 이 부분이 문제다. Make pc 했을때 생기는 에러는 TIMER_PRESCAL을 쓰면서 define을 해놓지 않아서 발생하는 문제이다.그렇다면 TIMER_PRESCAL을 왜 쓸려고 했을까? 그건 원래 UCB에서

의 소스를 보고 확인할수 있는데,원본은 아래와 같다.

Command result_t StdControl.start() {uint16_t randomtimer;call CC2420Control.SetRFPower(15);call MacControl.enableAck();randomtimer = (call Random.rand() & 0xffff) +1 ;return call Timer.start(TIMER_ONE_SHOT, randomtimer);

}

CC2420의 RF power를 설정하고 MAC protocol에 ack를 enable 시키

고 random하게 timer를 ONE_SHOT으로 동작시킨다. ONE_SHOT은 한번만 실행한다는 얘기다. 그럼 여기서 왜

randomtimer를 쓰는 이유는?수많은 노드들이 초기에 같이 동작하게 되면 동시에 겹치게 될것이

다. 그래서 처음 한번은 랜덤한 시간을 가지고 센싱을 하고 그후부

터는 TIMER_REPEAT 로 일정 간격으로 수행하면 될것이다. Random을 쓰기 위해서는 lib에 보면 제공해 주는 라이브러리가 있다.

이제 어떻게 쓰는지 상세히 보자.우선 component는 RandomLFSR를 쓰면 된다. 이건 찾아보면 알수

있다.

Page 53: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 50 -

RandomLFSR를 쓰기 위해서 SurgeM.nc에서

uses interface Random;

을 추가해준다.다음 Surge.nc의 configuration-implementation에서 component 추가

를 해준다. components Main, SurgeM, TimerC, LedsC, NoLeds, Photo, RandomLFSR, 그 밑에다가 wiring 하자.

SurgeM.Random -> RandomLFSR;

이것으로 끝이다. 이제부터 우리는 RandomLFSR component를

Random이란interface를 통해서 사용할 수 있다. RandomLFSR 내에는 이미 Random interface가 제공되어져있다.

rand() 함수도 확인해보자.

모든 데모 App에 거의 공통적으로 들어가는 부분인데, 이것을 간소

화해서 TIMER_PRESCAL 로 대체해서 쓸려고 했던 것 같다. 그런데

문제는 TIMER_PRESCAL을 정의 해놓은 부분이 들어 있지 않다는 것

이다.

다시 원소스 분석으로 들어가겠다.

Page 54: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 51 -

Timeout event가 발생하면 ADC로부터 센싱된 데이터를 가져오라는

말이다.

센싱이 끝나서 데이터가 준비되면 발생하는 event는 dataReady() 이

다.

센싱된 데이터를 radio로 보내기 위해서 task를 queue에 post 한다.

Task queue에 들어간 SendData()는 queue에 쌓인 순서대로 수행되

게 된다.SendData()에서는 Tos_Msg내에 MultiHop_Msg 밑에 Surge_Msg구조

Page 55: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 52 -

체에 맞게 데이터를 넣는다. 이것은 MultiHopEngineM.nc 파일의

getBuffer()를 보면 나와있다.

Data의 처음 부분을 넘겨준다. 그럼 그 다음에다가 Surge_Msg를 넣

으면 될것이다.

Send.send()도 MultiHopEngineM.nc에 구현되어있다.

MultiHopEngineM.nc에서는 자체적으로 시작하는 내용보다는 event에

의한 동작과 SurgeM 에서의 function호출에 의한 동작이 주가 되므

로 먼저 MultiHopLEPSM.nc 를 살펴보도록 하자.

Page 56: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 53 -

먼저 AM.h에 가보면 각 주소에 대한 설정이 되어있다.

위의 소스를 보면 자신이 BASE_STATION_ADDRESS를 가지면, 즉

sink node일때와 아닐 때 두가지에 따라 초기화 값이 달라진다. 먼저 sink node 일때 부모는 UART(0x007e)가 되어야 할것이다.Sink node가 아닐때는 부모가TOS_BCAST_ADDR(0xffff)가 되어야 할

것 같다.다른 소스에 보면 그렇게 되어있다. 찾아서 확인해보길~그외에 다른 변수중에 HopCount설정부분이 sink node 일때는 0, 아닐때는 ROUTE_INVALID (0xff) 로 설정하는 부분이 틀리다. Init()후에 start()가 호출 될것이다. 찾아가서 보자.

위에서 gUpdateInterval은 init()에서

gUpdateInterval = DATA_TO_ROUTE_RATIO * DATA_FREQ;라고 설정되어 있다. 여기서 한가지 의문은 Start()에서 왜 post TimerTask()를 할까?왜 그런지는 TimerTask()를 살펴보면 알수 있다. 우선 쉽게 얘기하자

면 처음 시작하면 Beacon을 받아서 자신의 sink node가 누구인지, 이웃들은 누가 있는지 등의 routing정보를 가졌을 때 TimerTask()를

통해서 table갱신과 부모를 선정하게 된다.

그런데 시작하자마자 TimerTask()호출해봤자 무엇이 있겠는가 처음

타이머 한번 돌고 타임아웃되면 하는게 맞을거 같다. 그래서 fired() 에서 수행을 하고 있다.

Page 57: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 54 -

함수 이름만 봐도 알수 있다. UpdateTable(), chooseParent() 여러분

도 다 짐작할수 있을것이다.

Page 58: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 55 -

위에 두 부분은 여러분이 천천히 보면 충분히 이해할수 있을것이다. 이해를 돕자면

이러하다.

마지막에 있는 post SendRouteTask()를 살펴보자.

Page 59: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 56 -

이부분을 보면 MultiHop_Msg안에 Route_Packet을 집어넣는 부분이

다.앞에서 나온 message 구조를 다시 확인해보자. neighborTable중에서 NBRFLAG_VALID 한것만을 sortTable에 넣는 것

을 볼수 있다. 그외 route packet 필드들을 채우는 과정과 마지막에는MultiHop message의 필드들을 채우고 있다.

그외에 sortEntry에 넣어뒀던 값들이 RoutePacket내에 estEntries에

SeqOf로 들어가는 것을 볼수 있다.

다음으로 RoutePacket을 받으면 어떻게 될까?

Page 60: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 57 -

Event로 ReceiveMsg.receive()가 발생한다. 소스는 아래와 같다. 지금까지의 역과정이므로 지금까지 잘 따라왔다면 이해하기 쉬울것

이다.

받는 부분은 이걸로 끝내고 처음에 보낸 RoutePacket에 대한 응답으

로 MAC level 에서의ACK를 받았을때를 알아보자.

ACK를 받았을 때 발생되는 이벤트는 SendMsg.sendDone()이다.

gfSendRouteBusy는 SendRouteTask()에서 TRUE로 셋팅했다가, 여기

서 FALSE로 바꾼다. 위에 가서 확인하기 바란다. 그렇다면 이놈의 의미는 packet을 전송

하기 위해 저장하는 buffer가 이제 다 전송되었으니 다른 packet을

전송하기 위해buffer를 사용해도 된다는 의미로 해석할수 있다.

지금까지는 초기화시 route packet을 주고 받는것에 대한 설명이었

다. 이제부터는 surge message를 주고 받는 부분을 살펴보자.

Surge Message에 대한 처리는 MultiHopEngineM.nc에서 주로 다루고

있다. 그외에 MultiHopLEPSM.nc 에서 설명하지 않은 부분들이 Surge_Msg 관련해서 사용되는 함수들이다. MultiHopEngineM의 초기화 부분을 먼저 보자.

Page 61: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 58 -

Init()에서 SubControl.init()을 호출하는 것을 볼수 있다. 이것은

MultiHopLEPSM.init()을 호출하는 것이다.

Initialize()에서는 forwarding을 위한 2중 buffer를 초기화 한다. 아래와 같이 버퍼와 head, tail position이 선언되어있다.

앞에서 말했듯이 SurgeM에서 Surge_Msg를 send()하면

MultiHopEngineM의 send()를 호출하게 된다.

과정을 보면 먼저 initializeFields()를 호출하여 메시지 내용을 채우고

selectRoute()를 호출하여 다음에 보내질 route path를 결정한다.

다음에 message를 radio로 보내게 된다. initializeFields()와selectRoute()는 MultiHopLEPSM에 구현되어 있다.

Page 62: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 59 -

initializeFields()에서는 originaddr과 sourceaddr, hopcount를 넣는다.

이 부분은 이해하기 좀 힘든 부분인데…차근차근 보기 바란다. 우선 자기 자신이 sink_node인 경우와 아닌경우가 있는데, 이것은

소스에서

gpCurrentParent->id != TOS_UART_ADDR

를 통해서 판정한다. 그 다음 sink_node가 아니라면 자신이 보내는 surge_msg인지 아니

면 다른 노드가 보내는걸 중계해주는건지를 구분해야한다. 이 부분

은 소스의

(pMHMsg->sourceaddr == TOS_LOCAL_ADDRESS) &&(pMHMsg->originaddr == TOS_LOCAL_ADDRESS)

를 통해서 판정한다.이 두 가지를 숙지하고 소스를 보기 바란다.

Page 63: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 60 -

제일 처음 부분은 네트워크 형성이 되지 않아 parent에 대한 설정이

없는 경우에 surge_msg를 보내는 경우이다.

Msg->addr = TOS_BCAST_ADDR

을 설정하는걸 볼수 있다. [Msg->add] 은 message를 보낼곳인데, 자신의 부모가 될것이다. 지금은 없으므로 broadcasting 한다. 다음 줄은 hopcount를 비교해서 loop에 빠져서 계속 돌고 있는경우

처리해주는 부분이다. 다음 부분은 중요한데, 우선 flsDuplicate에 대해서 알아보자. 자세한

건 밑에서 설명하기로 하고, 우선 flsDuplicate은 bool 으로 중복된

메시지인지 아닌지를 나타낸다.

그런데 헷갈리기 쉬운건 중복된 메시지면 이값이 TRUE가 된다는 것

이다. 헷갈리지 말자.소스로 다시 가서 보면 if( ){ }else{} 이런 형일 것이다.

If(자신이 만들어낸 메시지이면){ flsDuplicate = FALSE //중복되지 않았으므로}Else //그외에는 다른노드에서 받은경우이므로{updateNbrCounters(); //중복된건지 체크해서 결과를 return 받는다.}

updateNbrCounters()에 대해서는 조금 있다가 자세히 다뤄보기로 하

자.

자 그럼 중복되었는지 아닌지에 대한 판정이 끝이 났으므로 이제

message를 보내주기만 하면 된다. 그 부분이 바로 젤 마지막에 있다. If(!flsDuplicate) 의 의미는 중복되지 않았다는 말이다.Message를 채우고 자신이 sink_node가 아니면 sequence number를

채우고, message를 보낼곳 Msg->addr을 자신의 부모 id로 넣는걸

볼 수 있다.

중복되었을때는 Result = FAIL로 set하고 Result를 return한다.

Page 64: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 61 -

앞에서 나온 updateNbrCounters()를 살펴보자.

앞에 내용을 다 이해했다면 이 부분도 쉽다. 우선 TinyOS는

message를 중계해줄 때 해당 message를 저장해놓는다. 거기가

NeighborTable이다. 그래서 이곳에선 지금 받은 message와 NeighborTable에 있는거랑

비교해서 같은게 있는지 확인해본다. 같은게 들어왔다는 말은 loop에

빠져 다시 온 거라고 볼 수 밖에 없을 것이다. 이런놈은 routing 해주면 안된다.그래서 그결과를 중복되면 TRUE로 중복아니면 FALSE로 return 해준

다.먼저 sDelta값을 확실히 알고 넘어가야한다. sDelta 값은

sDelta = (seqno - NeighborTbl[iNbr].lastSeqno - 1);

이렇다. 여기도 헷갈리기 쉬운 부분인데 숙지해야할 사항은 sDelta =0, >0, <0 이렇게 세가지 경우다.=0 : 정상적인 다음 seqno를 가진놈이 들어왔다는 거다. (마지막에

-1 해줬으므로)>0 : 중간에 몇놈이 가출했다.(error로 인해 잃어버렸든 어쨌든, 그

래도 서비스는 해줘야함)<0 : 더 낮은 seq_number를 가진놈이 들어왔다는 얘기다. 이건 두

가지 경우로 나뉜다.

Page 65: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 62 -

참고로 ACCEPTABLE_MISSED = -20 이다. 뒤에 설명 나온다. 여기서 우선 언급하고 지나가겠다.

If(flag를 통해서 처음들어온 이웃인지 알수 있다){ 처음 들어왔으니 방한칸 마련해주자 }

Else if(중복이 아닌 경우){ sDelta>=0 이므로 우쨋든 서비는 해주자 대신 missed에 가

출여부를 적어놓자 }

Else if(-20보다 작다는 말은 노드가 초기화 되었다고 본다. 예를 들어 해당노드가 power off했다가 on하면 초기화 될것이다)

{ 해당노드에 대한것들은 우리도 reinitialize해주자}

Else(위에 없는경우 0>sDelta>=-20 이 경우가 해당할것이다){ -1은 바로 전 데이터의 중복이다, 그외 -2 ~ -20까지도 중복이라고 본다.}

보내는건 이걸로 끝이다. 정리해보자

Surge_Msg를 보내기 위해 send()호출하면 MultiHopEngineM.send()가

호출되고, 그 안에서 initializeFields()에 의해서 origin내용이 채워지

고, selectRoute()에 의해 다음 보낼곳을 정하게 된다. 마지막으로

Radio로 보내면 끝이다.

지금부터는 Surge_Msg를 받았을 경우를 알아보자. Surge_Msg를 받으면 발생하는 event는 MultiHopEngineM의

ReceiveMsg.receive() 이다.위에서 본 MultiHopLEPSM에 있는 ReceiveMsg.receive()랑은 이름은

같지만 틀린놈이다. 우선 surge에서 사용하는 message는 두가지이다. 라우팅을 위한

Route_Packet과 실제 센싱 데이터를 보내기 위한 Surge_Msg 이다. MultiHopEngineM의 ReceiveMsg.receive()는 Surge_Msg를 받았을 때

처리하는 곳이고, MultiHopLEPSM의 ReceiveMsg.receive()는

Route_Packet을 받았을 때 처리하는 곳이다.

Page 66: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 63 -

자신에게 온 message가 맞는지 확인하고 forwarding 시킨다. Intercept signal을 날리는 것은 구현 안되어 있으므로 의미 없다.Forwarding의 의미는 예를들어3 -> 2 -> 1 -> 0 (sink_node) 로 전

송할 때 3은 2로 forwarding하고 2는 1로 forwarding한다고 한다.

그럼 mForward()부분을 살펴보자.

If(FwdBufList가 비어있는지 체크하는 부분이다. 이중버퍼가 사용중이

면){

더 이상 진행하지 않고 그냥 return: forwarding 하지 않는다

}비어있으면 다음으로 넘어가보자

If(selectRoute()를 통해서 경로를 못찾으면 ){

마찬가지로 forwarding 하지 않는다. }경로 찾았으면 다음으로 넘어가자

If(send()에 성공하면)

Page 67: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 64 -

{이제 다음 send에 사용해야하니 FwdBufList를 pNewBuf에 넣

자.그리고 내가 보낸 message인 pMsg를 FwdBufList안에 넣어두

자.}Send()에 실패하면 결국 forwarding 하지 않는다. return pNewBuf;

MAC에서 ACK를 받으면 sendDone() event가 발생한다. sendDone() event에는 message의 ACK정보와 success 정보를 가지고

있다.

여기를 보면 실패했을때의 구현이 안되어있는 것 같다. 성공했을때는Forwarding Buffer를 비워주는것으로 끝난다.

6.5. Event Library 분석

지금부터 TinyOs의 Library 중 Event Library를 분석해 본다.

먼저 "tinyos-1.x tos lib Events"₩ ₩ ₩ 폴더를 보면 "Event.nc", "EventM.nc" 두 개의 파일이 존재함을 확인할 수 있다.

두 개의 파일 중 Event.nc의 내용을 먼저 확인해 보자.

Page 68: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 65 -

내용을 보면 그리 특별한 것은 없어 보인다. Event가 제공해주는 인

터페이스의 종류(StdControl, EventRegister, EventUse)와 컴포넌트들

(Commnad, EventM, LedsC)과의 연결을 확인할 수 있다.여기까지 봤을 때 Event 라이브러리의 내용이EventM.nc 파일에 거의

모든 것이 구현되어 있음을 대충 짐작할 수 있다.

EventM.nc 파일을 확인해 보자. 소스가 꽤 길다. 처음부터 차근차근

접근해 보도록 하자.

EventM이 제공하는 인터페이스와 사용하는 인터페이스를 정의하는

부분이다.

다음으로 implementation 부분을 살펴보자.먼저 변수를 정의하는 부분이다.

변수를 정의하는 부분에서 쉽게 접하지 못하는 타입을 볼 수 있다. EventDesc, EventDescs, ParamVals, EventQueue 타입들이 어떤 것들

인지 타입이 정의된 부분을 찾아보자.

이 중 EventDesc와 EventDescs 타입은 "tinyos-1.x tos interfaces" ₩ ₩

폴더의 Evnet.h 파일에 정의되어 있다.

Page 69: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 66 -

EventDesc 타입은 변수명에서 대충 짐작할 수 있듯이 Event의 내용

을 정의하는 구조체이다.하지만 각 변수의 정확한 역할은 아직 모른다. 차츰 Event 라이브러

리를 분석해가면 살펴보자.

구조체 안의 변수들 중 ParamList 라는 타입을 가진 변수가 보이는

데, 이 타입은 "tinyos-1.x tos interfaces" ₩ ₩ 폴더의 Params.h 파일에

다음과 같이 정의되어 있다.

참고로 TOSType 타입은 "tinyos-1.x tos interfaces" ₩ ₩ 폴더의

SchemaType.h 파일에 다음과 같이 정의되어 있다.

TOSType 타입은 열거형이며, 0-10까지 정의되어 있음을 확인할 수

있다.

다음은 EventDescs 타입을 정의한 부분이다.

Page 70: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 67 -

단순히 Event의 개수와 EventDesc 타입의 배열이 구조체로 정의되어

있음을 확인할 수 있다.

ParamVals 타입은 "tinyos-1.x tos interfaces" ₩ ₩ 폴더의 Params.h 파일

에 정의되어 있다.

파라미터의 수를 저장하는 uint8_t형의 변수와 파라미터Data들의 포

인터 list들을 저장하는 char형의 포인터 배열이 구조체로 정의되어

있다.

마지막으로 EventQueue 타입을 살펴보자.EventQueue 타입은 "tinyos-1.x tos interfaces" ₩ ₩ 폴더의 Evnet.h 파일

에 다음과 같이 정의되어 있다.

EventQueue는 EventInstance를 저장하는 Queue를 정의한 타입임을

확인할 수 있다. 그리고 EventInstance는 EventDesc의 포인터를 저장

하는 EventDescPtr형의 변수와 ParamVals형의 변수로 구성된 구조체

라는 것도 알 수 있다.

전역변수에 대한 설명이 끝이 났다. 이제부터 본격적으로 코드를 처

음부터 하나씩 차근차근 살펴보도록 하자.

변수 설정 다음으로 나오는 부분은 eventQueueInit() 함수이다.

Page 71: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 68 -

이 함수는 간단하게 전역 변수로 설정한 eventQueue를 초기화 하는

함수이다. 초보라도 쉽게 알 수 있는 부분일 것이다.

그 밑으로는 eventEnqueue() 함수가 보인다.

eventEnqueue 함수는 함수 이름을 보면 짐작할 수 있듯이

eventQueue에 EventDescPtr 타입의 데이터를 집어넣는 함수이다. 리

턴 타입으로 result_t 타입을 반환한다.

처음으로 현재 eventQueue의 size를 검사하여 큐에 저장 가능한 장

소가 있다면 계속 진행하고, 저장할 장소가 없다면 ‘FAIL’을 반환하고

끝낸다.그리고 현재 eventQueue의 요소인 inuse의 값이 TRUE이면 큐에 저

장하지 않고 ‘FAIL’을 반환하고 끝낸다.

큐의 유용성을 체크하고 난 후, 우선 eventQueue의 inuse 요소의 값

을 TRUE로 바꾼다. 이는 큐에 어떤 이벤트를 저장하고 있는 중에는

다른 이벤트를 저장하지 못하도록 하여 두 개 이상의 프로세스가 동

시에 이 함수를 호출했을 때 발생할 수 있는 error를 미연에 방지하

기 위해 필요한 부분이다.

다음으로는 큐에 데이터를 저장하고 size를 1 증가시키는부분이다. 이 부분은 C프로그래밍에서의 원형큐에 데이터를 저장하는 부분과

다를 바가 없기 때문에 상세설명은 생략하겠다.

데이터를 모두 저장하고 나면 inuse를 FALSE로 바꾸고 ‘SUCCESS’를

반환한다.

Page 72: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 69 -

위의 코드는 eventDequeue 함수의 모습이다. 이 함수는 현재

eventQueue에 저장되어 있는 데이터를 빼서 가져오는 함수이다. 리

턴 타입으로 result_t 타입을 반환한다.

데이터를 큐에서 빼내오는 역할을 하는 함수이기 때문에 가장 먼저

큐에 데이터가 있는지 체크(if (eventQueue.size == 0))를 한다. 큐에

꺼내올 데이터가 없다면 ‘FAIL’을 반환하고 끝낸다.

이 함수 역시 두 개 이상의 프로세스가 이 함수를 호출했을 때 발생

할 수 있는 error를 미연에 방지하기 위해 eventQueue의 inuse 요소

를 이용한다.그리고 큐의 head에 있는 eventInstance 형의 데이터를 함수의 인자

인 *eventDesc와 *params에 저장한다.

다음으로는 큐의 사이즈를 줄이고 head값을 하나 더하는 부분인데, 이는 C 프로그래밍에서 원형 큐를 구현하는 것과 같으므로 상세 설

명은 생략하겠다.

Page 73: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 70 -

위 코드는 StdControl command를 구현해 놓은 부분이다.

StdControl.init() 에서는 eventDescs를 비롯한 전역 변수들의 초기값

을 저장한다.StdControl.start()와 StdControl.stop()는 단순히 ‘SUCCESS’를 반환한

다.

다음은 EventUse.getEvent command이다

EventUse.getEvent()는 char 형의 name을 인자로 받는다. 그리고 for문을 이용하여 현재 eventDescs에 저장되어 있는 EventDesc 형의 데

이터 수만큼 반복하면서인자로 받은 name과 EventDesc 형의 데이터

의 name 요소와 값을 비교하여 같은 값이면 그 데이터를 반환하는

command이다.

① 은 EventDesc 타입의 deleted 요소 값이 FALSE 인지 확인하는 부

Page 74: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 71 -

분으로 이벤트가 이미 지워진 이벤트인지 검사하는 부분이다.② 는 strcasecmp 함수를 이용하여 인자로 받은 name의 값과 이벤

트의 name과 비교하여 같은지를 검사하는 부분이다.

☞ <참고> - strcacsamp

#include <string.h>

int strcasecmp(const char *s1, const char *s2);

대소문자를 구분하지 않고, 두 문자열 s1과 s2를 비교한다.▦ 반환값 :

s1과 s2가 같으면 0, s1이 s2보다 크면 0보다 큰 정수 s1이

s2보다 작다면 0보다 작은 정수를 반환한다.

위 코드는 EventUse.getEventByID command를 구현한 부분이다.uint8_t 형식의 id를 인자로 받아서 eventDescs에 저장되어 있는 이

벤트 중 id와 같은 인덱스에 위치하는 이벤트를 반환하는 command이다. 만약 id가 유효하지 않다면 NULL을 반환한다.

① 은 인자로 받은 id가 유효한지 검사하는 부분이다.

다음으로 나오는 EventUse.getEvents와 EventUse.numEvents command는 아주 간단하다.

EventUse.getEvents() command는 전역변수인 eventDescs의 주소를 반

환한다. 이것은 현재 저장되어 있는 EventDesc 형의 데이터들을 모두

Page 75: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 72 -

반환한다는 뜻임을 알 수 있을 것이다.EventUse.numEvents() command는 eventDescs의 numEvents 요소의

값을 반환한다. 이것은 현재 저장되어 있는 EventDesc 형의 데이터들

이 몇 개 인지를 반환한다는 뜻임을 쉽게 눈치 챌 수 있을 것이다.

위 코드는 signalEventTask() task를 구현한 부분이다.signalEventTask()는 이벤트를 실질적으로 처리하는 Task이다.

우선 현재 처리해야 할 이벤트(currentEventDesc)가 있는지 체크하는

부분이 ① 부분이다. 만약 처리해야 할 이벤트가 있으면 char 형의 name 변수에 현재 이벤트의

name을 입력하고, ②부분에서 보는 바와 같이 CommandUse.InvokeById()

Page 76: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 73 -

를 호출하여 currentEventDesc의 cmds[] 요소에 저장되어 있는 command를 처리한다.

여기서 잠깐, CommandUse.InvokeById()를 살펴볼 필요가 있을 것 같

다.

CommandUse.InvokeById()는 “tinyos-1.x₩tos₩ lib₩Commands₩Command.nc” 에 정의되어 있다.

소스를 살펴보면, 우선 commandDesc 변수에 CommandUse.getCommandById()를 호출하여 CommandDescPtr 타입의 데이터를 저장하고 이 변수의

값이 NULL이거나 commandDesc의 params.numParams 값이랑 인자로

받은 params의 numParams의 값이 다르면 FAIL을 반환한다. 밑의 코

드는 CommandUse.getCommandById의 소스 부분이다.

그리고 Cmd.commandFunc event를 호출한다. 이때 인자는 commandDesc의 name요소, resultBuf, errorNo, params 이며, 반환된 값이 SUCCESS가

아니면 FAIL을 return한다.밑의 소스는 Cmd.commandFunc() event이다.

또한 errorNo의 값이 SCHEMA_ERROR이면 FAIL을 return한다.errorNo의 타입인 SchemaErrorNo는 SchemaType.h에 다음과 같이 정

의되어 있다.

Page 77: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 74 -

이쯤에서 CommandUse.InvokeById()의 설명을 끝내고 계속해서

signalEventTask()를 살펴보자.

CommandUse.InvokeById()를 호출하여 command를 처리하고 그 결과

값이 SUCCESS가 아니거나 errNo의 값이 SCHEMA_ERROR 일 경우

currentEventDesc의 값을 NULL로 바꾸고 EventUse.eventDone event를

name과 SCHEMA_ERROR를 인자로 하여 호출한다.

만약 command를 처리한 결과값이 앞서 설명한 경우가 아니라면 ③ 부분과

같이 다시 한번 errNo의 값을 체크하는데 그 값이 SCHEMA_RESULT_PENDING이면 currentCmdDone의 값을 FALSE로 준다. 이 부분은 command를 처리하고 결과가 확실하게 나지 않았을 경우

현재 이벤트를 생략하고 다음 이벤트를 처리하기 위한 작업인 것 같

다.

command를 정상적으로 처리하였을 때 nextCmdIdx를 1증가 시키고

혹시, 이 값이 유효한지체크하는 부분이 ④ 이다. currentEventDesc의

numCmds의 값과 비교하여 크면 현재 이벤트에 포함된 command를

모두 처리한 것이기 때문에 currentEventDesc의 값을 NULL로 바꾸고

EventUse.eventDone event를 호출한다. 이때 인자는 name과

SCHEMA_SUCCESS 값이다.

⑤ 에서는 currentEventDesc가 NULL인지를 체크하는 부분인데 만약

NULL이면 ⑥ 에서와 같이 eventDequeue 함수를 호출하여 eventQueue에 저장되어 있는 EventDesc 타입의 데이터를 가져온다. 이때 데이터

를 가져오지 못하고 FAIL이 반환 된다면 eventQueue에 저장된 이벤트

가 없다는 뜻이므로 signalEventTask를 모두 끝낸다.

⑦ 은 eventQueue에서 가져온 데이터를 이용하여 전역변수인

currentEventDesc, currentParams, currentCmdDone, nextCmdIdx의 값

들을 설정하는 부분이다.

마지막으로 ⑧ 은 남은 현재 이벤트의 command를 처리하거나 다음

이벤트를 처리하기 위하여 signalEventTask()를 호출하는 부분이다.

다음으로 구현되어 있는 EventUse.signalEvent() command를 보자.

Page 78: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 75 -

EventUse.signalEvent()는 EventUse.getEvent()를 호출하여 EventDescPtr타입의 데이터를 가져온 후 이 데이터의 유효성을 체크하고, eventEnqueue()함수를 이용하여 eventQueue에 저장하는 command이

다. 그리 어렵지 않아서 자세한 설명은 하지 않아도 될 듯하다.

이어서 EventUse.signalEventMsg() command를 분석해보자.

EventUse.signalEventMsg() command는 어떤 역할을 하는 함수인지 정

확히 파악하지 못하였다. 짐작하건대 TOS_Msg를 인자로 받아서 이

메시지를 이벤트의 이름, 파라미터로 parsing을 하고 이 이벤트를

signal 하기 위한 command인 듯 하다.

우선 ① 에서 eventMsgPending값을 검사하여 TRUE이면 FAIL을 return하고 끝낸다. FALSE이면 ② 가 실행되어지는데 TOS_Msg를 parsing 하기 위한 초기작업을 하는 부분이다.msgCopy의 값을 인자로 받은 TOS_MsgPtr 타입의 데이터가 가리키는 값

으로 입력하고, cmsg의 값을 msgCopy의 data요소를 struct CommandMsg* 형태로 타입변환 하여 입력한다.아래 소스는 TOS_Msg와 struct CommnadMsg를 정의한 부분이다.

Page 79: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 76 -

③ 에서 cmsg의 nodeid 요소 값이 TOS_BCAST_ADDR 이거나

TOS_LOCAL_ADDRESS 인지를 체크한다. 체크한 결과가 TRUE이면 ④에서 보는 바와 같이 cmsg의 data[0]의

주소를 char * 형으로 변환하여 ptr에 저장하고 이 값을 char * 타입

의 eventName 로컬변수에 저장한다.그리고 eventName을 이용하여 EventUse.getEvent()를 호출하여 반환

된 EventDesc 타입의 데이터를 eventDesc 로컬변수에 저장한다.

⑤의 for문에서는 eventDesc의 params.numParams의 값만큼 반복하면서

TOS_Msg에서 파라미터의 값에 해당하는 데이터를 ParamVals.paramDataPtr[i]에 저장한다.

⑥에서는 파라미터의 값들을 모두 저장하고 난 후, paramVals.numParams의 값을 eventDesc의 params.numParams 값으로 저장하고 eventName과

&paramVals를 인자로 한 EvnetUse.signalEvent() command를 호출하여

parsing한 이벤트를 이벤트 큐에 저장한다.

Page 80: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 77 -

위의 코드는 EventUse.registerEventCallback() command 이다. 이

command는 char *형의 eventName과 cmdName을 인자로 받아서

EventDesc 타입의 command 요소를 구성하는 작업을 한다.

①은 인자로 받은 eventName을 이용해 EventUse.getEvent()를 호출

하여 반환된 값을 eventDesc 로컬변수에 저장하는 부분이다.다음으로 ②에서는 ①에서 반환받은 eventDesc가 NULL 이거나

eventDesc의 numCmds 요소의 값이 MAX_CMD_PER_EVENT 보다 크다

면 FAIL을 반환하고 끝낸다.

③에서는 인자로 받은 cmdName을 이용해 CommandUse.getCommand()을

호출하여 반환된 값을 cmdDesc 로컬 변수에 저장한다.이렇게 받은 cmdDesc가 NULL인지를 체크하고 NULL이면 FAIL을 반환

하고 끝내는 부분이 ④이다.

이벤트의 파라미터 개수와 Command의 파라미터 개수는 같아야 하고

만약 다르면 FAIL을 반환하고 끝내야 한다. 이 작업을 처리하는 부분

이 ⑤이다.

다음으로 for문을 이용하여 이벤트의 파라미터들과 Command의 파라

미터들이 각각 같은 TOSType 인지를 체크한다. 이 부분이 ⑥이다.

유효성 체크가 모두 끝이 나면 마지막으로 ⑦에서와 같이 eventDesc의

cmds요소에 cmdDesc의 idx요소를 저장하고 eventDesc의 numCmds요소를 1증가 시킨다.

Page 81: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 78 -

다음으로 살펴볼 것은 EventUse.deleteEventCallback() command이다.이 command는 char * 타입의 eventName과 cmdName을 인자로 받아

서 eventName과 같은 이벤트를 찾고 그 이벤트를 구성하고 있는

command 중 cmdName과 같은 command를 delete한다.

①은 인자로 받은 eventName을 이용하여 이벤트를 찾는 부분이다. eventDesc 로컬 변수에 EventUse.getEvent(eventName)을 호출하여 받

은 데이터를 저장하고, eventDesc의 값이 NULL 이면 FAIL을 return하

고 끝낸다.

②부분은 인자로 받은 cmdName을 이용하여 command를 찾고 그

command의 값이 NULL 인지 체크한다.cmdDesc 로컬 변수에 CommandUse.getCommand(cmdNAme)을 호출

하고 return받은 데이터를 저장한다. 그 후 cmdDesc의 값이 NULL이면

FAIL을 return하고 끝낸다.

③은 for문을 이용하여 eventDesc의 numCmds요소의 값만큼 반복하면

서 eventDesc의 cmds요소 값들 중 cmdDesc의 idx요소의 값과 같은

것이 있는지 체크하고 있으면 for문을 빠져나온다. 이것은 현재 이벤

트를 구성하고 있는 command 중 지우려고 하는 command의 index를 찾기 위한 작업이다.④는 for문을 빠져 나왔을 때 cmd 값이 eventDesc의 numCmds의 값

보다 큰 지 체크하고 크면 FAIL을 return하고 끝내는 부분이다. 이것

은 for문을 모두 반복하고 나왔을 때 eventDesc의 cmds 요소 값들 중

지우려는 command가 없을 경우를 체크하기 위한 로직이다.

⑤는 for문을 이용하여 eventDesc의 cmds 배열을 다시 재구성하는 부

Page 82: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 79 -

분이다. 만약 지우려고 하는 index보다 뒤에 있는 데이터들을 앞으로

이동시켜 cmds배열을 재구성한다.

마지막으로 ⑥에서와 같이 eventDesc의 numCmds요소의 값을 1감소

시키고 SUCCESS를 return한다.

계속해서 EventRegister.registerEvent() command를 살펴보도록 하겠

다.

EventRegister.registerEvent() command는 char* 형의 evnetName과

ParamList * 형의 params를 인자로 받아서 eventDescs에 이벤트를 등

록하는 command이다.

①에서 인자로 받은 params의 numParams의 값이 MAX_PARAMS의 값

보다 큰 지 체크하고 만약 크면 FAIL을 return하고 끝낸다.

②에서는 addEventPending의 값과 eventName의 length가

MAX_EVENT_NAME_LEN 보다 큰 지를 체크한다. 만약 addEventPeding

Page 83: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 80 -

의 값이 TRUE이거나 eventName의 length가 MAX_EVENT_NAME_LEN 보다 크면 FAIL을 return하고 끝낸다. 그렇지 않다면 addEventPending의 값을 TRUE로 바꾸고 계속해서 작업을 진행한다.addEventPending 로컬 변수는 두 개 이상의 프로세스가 동시에 이 작

업을 호출하였을 경우 발생할 수 있는 error를 방지하기 위한 bool형

변수이다.

③에서는 for문을 이용하여 전역변수인 eventDescs에 저장되어 있는

이벤트 중 지워진 이벤트가 있는지, 또는 저장하려는 이벤트의 이름

과 같은 이름을 가지고 있는지 검사한다.

④부분이 eventDescs.eventDesc[eventIdx].deleted 의 값을 체크하여

현재 eventDescs에 저장되어 있는 이벤트 중 deleted 된 이벤트가 있

는지 검사하는 부분이다. 만약 eventDesc[] 중간에 deleted 된 이벤트

가 있다면 그 곳에 새로운 이벤트를 등록해야 하므로 로컬 변수인

eventDesc에 배열의 deleted 된 장소의 주소 값을 저장한다.

⑤부분이 eventDescs.eventDesc[eventIdx].name과 인자로 받은

eventName 값을 비교하여 저장하려는 이벤트의 이름이 이미 저장되

어 있는지를 체크한다. 만약 같은 이름을 쓰는 이벤트가 저장되어 있

다면 eventExists의 값을 TRUE로 바꾸고 for문을 빠져나온다.

⑥의 if 문은 eventExists의 값을 체크한다. 만약 eventExists의 값이

FALSE이면 이벤트가 존재하지 않는 것이므로, 새로운 이벤트를 저장

하는 것이 된다. 그래서 eventDescs.numEvents의 값을 1증가 시켜준

다. 만약 eventDescs.numEvents의 값이 MAX_EVENTS 보다 크거나 같

으면 addEventPending의 값을 FALSE로 바꾸고 FAIL을 return한다.

⑦에서는 ③의 for문 내에서 eventDesc의 값을 저장하지 못하였을 경

우 eventDesc를 지정해준다.

마지막으로 ⑧에서 이벤트의 값들을 모두 setting한다. 그리고

SUCCESS를 return한다.

Page 84: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 81 -

EventRegister.deleteEvent() command는 eventDescs에 등록되어 있는

이벤트 중 인자로 받은 name과 같은 이름을 쓰는 이벤트를 delete하

는 작업을 한다.

①에서 보는 바와 같이 for 문을 이용하여 eventDescs에 등록된 이벤

트의 수만큼 반복하면서 ②의 조건문에서 deleted의 값이 FALSE이면

서 인자로 받은 name 값과 이벤트의 값이 같은지를 체크하고 조건을

만족하면 eventDesc[i]의 deleted의 값을 TRUE로 바꾸고 SUCCESS를

return한다.

for 문이 끝나도록 이벤트를 지우지 못한 경우에는 FAIL을 return 한

다.

마지막으로 살펴볼 것은 CommandUse.commandDone() event이다.

CommandUse.commandDone() event는 command가 처리되었을 때 발

생하는 event이다.

①의 조건문은 currentCmdDone가 TRUE이거나 currentEventDesc가 NULL일 때 SUCCESS를 return하는데, currentCmdDone과 currentEventDesc의

초기값이 각각 TRUE와 NULL이므로 이 조건은 어떤 이벤트의 command가 처리되지 않았을 때를 말한다. 그래서 아무런 작업도 하지 않고 단순

Page 85: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 82 -

히 SUCCESS를 return하는 것이다.

②에서는 먼저 인자로 받은 errorNo의 값을 체크하는데 errorNo의 값

이 SCHEMA_ERROR일 때, currentEventDesc와 currentCmdDone의 값

을 초기화 시키고 EventUse.eventDone() event를 호출한다. 이때 인자

는 name과 SCHEMA_ERROR이다. 즉, 이벤트가 제대로 처리되지 않

고 error가 발생했다는 것이다.

③은 command가 제대로 처리 되었을 때 동작을 하게 되고, command는 처리가 제대로 되었기 때문에 currentCmdDone의 값을

TRUE로 초기화 시켜준다.

④의 조건문에서는 currentEventDesc에 처리 할 command가 남아 있는

지 체크한다. 만약 모든 command가 처리 되었다면 currentEventDesc의 값을 초기화 시키고 EventUse.eventDone() event를 호출한다. 이때

인자는 name과 SCHEMA_SUCCESS이다. 아직 처리할 command가 남아

있다면 현재 이벤트가 모두 처리된 것이 아니기 때문에 마지막에

SUCCESS만 return한다.

Page 86: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 83 -

6.6. MultihopRouter & MultiHopEngineM Component6.6.1. Interfaces

위의 그래프를 통해 MultihopRouter는 아래와 같은 6개의 인터

페이스를 제공하고 1개의 외부 인터페이스를 사용하는 것을

확인 할 수 있다.제공 인터페이스

- StdControl, Receive, Send, Intercept, Intercept as Snoop, RouteControl

사용 인터페이스

- ReceiveMsg그리고 이것은 MultihopRouter.nc 파일을 통해서도 확인 할 수

있다

Page 87: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 84 -

☞ CHECK - Component Graph의 화살표☞ CHECK - Component Graph의 화살표☞ CHECK - Component Graph의 화살표TinyOS의 Component Graph에서 각 Component사이의 화살표 중에서

점선 화살표와 실선 화살표의 의미는 다음과 같다

- 점선 : (=) Equal을 의미한다. 다시 말해서 화살표가 시작하는 컴포넌

트의 Interface는 직접적으로 하는 일이 없고 화살표가 끝나는

컴포넌트의 Interface가 해당하는 작업을 수행한다.

위의 MultihopRouter의 경우 MultihopRouter의 Interface대신

MultihopEngineM의 인터페이스가 작업을 수행하게 된다. 실제

구현부의 소스를 확인해 보면 다음과 같다

- 실선 : (->) 연결, 호출을 의미한다. 다시 말해서 화살표가 시작하는

컴포넌트의 Interface가 호출되면 화살표가 끝나는 컴포넌트의

인터페이스가 자동으로 호출되는 것을 의미한다.

위의 MultihopRouter의 경우 MultihopRouter를 제외한 나머지 컴

포넌트들은 이와 같이 연결되어 있음을 확인할 수 있다. 실제 구

현부의 소스를 확인해 보면 다음과 같다.

6.6.2. Multihop Message(MultiHop.h)Multihop Message의 구조를 확인하기 위해 TOS_Msg의 구조와

함께 살펴보면

Page 88: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 85 -

위와 같은 구조체로 구성되어 있는 것을 확인할 수 있고, TOS_Msg 중에서 data에 해당하는 부분에 Multihop Msg가 실

려서 전송되는 구조를 가지고 있다. Multihop Msg의 구조체는

아래와 같이 구성되어 있다.

6.6.3. MultiHopEngineM의 Initialize위의 그래프에서 보았듯이 MultihopRouter에서 제공되는 인터

페이스는 MultiHopEngineM에 모두 구현되어 있다. 따라서

MultiHopEngineM.nc 의 소스를 살펴보도록 하겠다.

위의 소스는 MultiHopEngineM.nc 파일의 시작부분이다. TOS_Msg가 정의되어 있었던 AM.h 와 MultiHopMsg가 구현되

어 있던 MultiHop.h가 include 되어 있는 것을 볼 수 있다.MultiHopEngineM에서 제공되는 interface와 사용되는 외부 인

터페이스의 목록도 확인할 수 있다.

Page 89: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 86 -

다음으로 inplementation 부분을 살펴보자.

위의 소스는 implementation중에서 초기화를 하는 부분이다.우선 내부함수로 initialize()가 구현되어 있는데 이 함수에서는

FWD_QUEUE_SIZE에 해당하는 개수만큼 FwdBufList를 생성한

후 iFwnBufHead 와 iFwdBufTail의 값을 0으로 초기화 해준다.그리고 StdContorl의 init()에서 initialize()가 호출되는 것을 확

인할 수 있다. 그리고 CommStdControl.init()을 호출하고

SubControil.init()을 호출하여 return 하는 것을 확인할 수 있

다. 이 부분을 자세히 살펴보면 MultiHopEngineM의 StdControl

Page 90: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 87 -

이 초기화 되면서 동시에 수행되는 일을 확인할 수 있는데. 그

림 4를 참조해보면, CommStdControl은 곧 StdControl을 말하

는 것이고 이것이 초기화 되면 Comm의 StdControl도 함께 초

기화 하도록 Wiring 되어 있는 것을 알 수 있다. 여기서

Comm은 GenericCommPromiscuous의 별칭이다. 그리고 그 후

에 SubControl을 초기화 하는데 예전 코드를 참조하여 보면

QueuedSend와 MultiHopLEPSM의 StdControl이 초기화 되도록

Wiring 되어 있음을 알 수 있다.여기서 왜 GenericCommPromiscuous의 StdControl은 SubControl을 이용해서 Wiring 하지않고 별도의 별칭을 사용해서 따로

Wiring 하였는지 의문점이 생기지만 뒤에 다른 구현을 편리하

기 위해 특별히 그렇게 했을 것이라는 짐작만 하고 일단 넘어

가겠다.다음으로 StdControl.start()를 살펴보면 CommStdControl과

SubControl의 start()를 수행해서 QueuedSend와

MultiHopEngineM의 StdControl의 start()를 수행한다는 것은 앞

서서 살펴본 init()을 통해 쉽게 파악할 수 있을 것이다.그리고 마지막으로 CommControl.setPromiscuous(TRUE)를 호출하

는 것을 볼 수 있는데 CommControl은 GenericCommPromiscuous에서 제공되는 interface라는 것을 그림 1의 그래프를 통해 알 수

있다. 그러면 GenericCommPromiscuous의 소스를 직접 살펴보겠

다.

Page 91: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 88 -

위의 소스를 살펴보면 CommControl이라는 인터페이스는 결국

AMPromiscuous에 있는 CommControl과 ‘=’로 연결되어 있는

것을 확인할 수 있다. 그렇다면 이것을

GenericCommPromiscuous의 컴포넌트 그래프를 통해 확인을

해 보자.

그림 Ⅱ-6-11. GenericCommPromiscuous Component Graph

위의 그래프를 통해서도 GenericCommPromiscuous의

CommControl을 비롯한 몇몇 인터페이스들이 AMPromiscuous의 그것들과 점선으로 연결되어 있는 것을 확인할 수 있다.

결국, CommControl.setPromiscuous(TRUE)은 AMPromiscuous의

CommControl 인터페이스에 구현되어 있는 setPromiscuous()를

호출하는 것 임을 알게된다.

setPromiscuous command의 내부를 보면 단순히 매개변수로

넘어온 bool 값을 promiscuous_mode의 값으로 세팅하는 것을

알 수 있는데 이 값을 세팅하는 이유는 다시 따로 다루도록

하겠다. 우선 여기서는 이러한 Wiring을 통해 결국

AMPromisecuous까지 오게 되는 과정을 이해하는 것으로 만족

하도록 하자.

다시 MultiHopEngineM으로 돌아가서 마지막으로

StdControl.stop()을 살펴보면 이 command는 특별히 특이한 사

항은 없고 init()에서 초기화 했던것과 반대로 CommStdControl과 SubControl을 stop()하고 종료하도록 되어 있다.

Page 92: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 89 -

☞ CHECK - GenericComm과 promiscuous_mode☞ CHECK - GenericComm과 promiscuous_mode☞ CHECK - GenericComm과 promiscuous_mode

TinyOS에서는 다수의 mote가 통신을 하면서 발생할 수 있는 다양한

간섭을 피하고 필요에 의해 mote의 그룹을 구성할 수 있게 하기 위해

다양한 변수에 의한 필터링을 지원한다.필터링 할 수 있는 요소로는

address(moteID), group, AM_type 등이 있다.

우리가 다수의 mote들로부터 데이터를 받아서 처리하기 위해 흔히 사

용하는 TOSBase는 address와 AM_type에 대해서는 필터링을 지원하지

않고 오로지 group에 의한 필터링만 지원한다.그리고 보통 일반적인

mote application들은 전송받은 데이터를 처리하기 위해

GenericComm을 사용하는데 이 GenericComm 컴포넌트의 경우

AMStandard 컴퍼넌트를 사용하는데 여기서 제공하는 receive()는

group ID, node ID, AM_MSG ID 모두를 필터링 하게 되어있다.

하지만 MultiHopEngineM에서 사용하는 GenericCommPromiscuous 컴퍼넌

트의 경우에는 AMPromiscuous 컴퍼넌트를 사용하는데 이 컴퍼넌트의

receive()는 기본적으로 node ID와 group ID를 체크하긴 하지만 node ID

는 선택적으로 검사하지 않게 할 수 있는데 이때 필요한 설정변수가

promiscuous_mode 이다

위 소스에서 promiscuous_mode가 TRUE일 경우 node ID에 대한 필

터링을 하지 않게 된다.

그리고 이 promiscuous_mode를 세팅할 수 있게 해주는 command가

위의 앞선 본문에서 알아본CommControl.setPromiscuous(TRUE) 이다.

Page 93: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 90 -

6.6.4. MultiHopEngineM의 commands6.6.4.1. Send

그림 15. MultiHopEngineM의 Send.send command

현재 Chapter에서 설명하고 있는 MultiHopEngineM의 command들

은 실제 메카니즘을 설명하기 보다는 전체적으로 어떠한 순서와 구

조로 돌아가고 있는지를 개략적으로 살펴보도록 하겠다. 결국은 각

자의 역할에 맞는 컴포넌트로 분기호출되어 각자 상세한 일을 처리

하기 때문에 그에 해당하는 상세한 설명은 해당 컴포넌트에 관한

Chapter에서 다시 상세히 다루도록 하겠다.

①번을 살펴보면 먼저 TOS_MHopMsg의 data 사이즈와

PayloadLen 사이즈를 더한 전체 메시지 길이를 구해서

TOSH_DATA_LENGTH 사이즈보다 큰지 체크하고 만약 크다면

FAIL을 반환하는 부분이다. 여기서 TOSH_DATA_LENGTH는

TOS_Msg에서 data 부분의 사이즈로서 AM.h에 보면 29로 정의되

어 있고 이 사이즈는 곧 Multihop msg의 사이즈이다.

①번에서 FAIL되지 않으면 그다음 ②번의

RouteSelect.initializeFields를 수행하는데 RouteSelect 인터페이스

는 MultiHopLEPSM에서 제공된다. initializeFields를 비롯한

MultiHopLEPSM에서 제공되는 command들은 나중에 별도의

chapter를 통해 자세히 알아보도록 하고 여기서는 특정한 id에 해

당하는 MultiHop Msg의 data와 sourceaddr, hopcount 등의 필드

값을 초기화 하는 역할을 한다는 정도만 알아두도록 하자.

②번이 성공하면 이제 ③번에 해당하는 RouteSelect의 SelectRoute

command를 실행하는데 이는 ②번에서 초기화 한 MultiHop msg

의 Route를 선택하는 역할을 한다. 실제로 이 command의 역할이

MultiHop Routing에 있어서 가장 중요한 부분인데 이것도 역시 이

Page 94: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 91 -

정도만 알아두고 별도의 Chapter를 통해 자세히 알아보도록 하자.

③번까지의 과정이 정상적으로 완료되면 이제 실제로 MultiHop

Msg를 전송하게 되는데 그부분이 바로 ④번 부분이다. SendMsg는

그림 1번의 그래프에서 보이는 QueuedSend 컴포넌트에서 제공되

는 인터페이스로서 Message Queue에 들어온 메시지를 실제로 전

송해주는 역할을 하는 인터페이스 이다.

소스에서 보는바와 같이 매개변수로 msg의 전달 주소와 메시지의

길이, 그리고 메시지 객체를 전달하는 것을 볼 수 있다. SendMsg

인터페이스에 대한 자세한 설명도 별도의 QueueSend 컴포넌트에 관

한 Chapter에서 다루도록 하겠다.

모든 과정이 완전히 성공하면 SUCCESS를 반환한다.

위의 getBuffer command는 단순히 해당하는 MultiHop Msg의 길

이를 구하여 반환해 주는 command이다.

6.6.5. ReceiveMsg.receive event

그림 17. MultiHopEngineM의 ReceiveMsg.receive event

receive 이벤트는 말 그대로 데이터가 node에 도착하였을때 발생하

는 이벤트 이다.

이벤트가 발생하면 TOS_Msg의 포인터가 전달되는데 이 포인터를

통해 해당 메시지의 data와 length 등에 접근하게 된다.

Page 95: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 92 -

Ⅱ. TinyOS Operation

1. TinyOS 란?

UC 버클리에서 진행해 온 스마트 더스트(Smart Dust) 프로젝트에 사용

하기 위하여 개발된 컴포넌트 기반 내장형 운영 체제(OS). 네트워크 내

장형 시스템을 위해 특별히 디자인된 초소형 open-source OS이다. 핵

심 OS 코드는 4000바이트 이하이고, 데이터 메모리는 256바이트 이하

이며, 이벤트 기반 멀티태스킹을 지원한다. 센싱 노드와 같은 초저전력, 초소형, 저가의 노드에 저전력, 적은 코드 사이즈, 최소한의 하드웨어

리소스를 사용하는 내장형 OS를 목표로 하며, 내장형 네트워크를 위한

프로그래밍 언어로는 nesC가 사용된다. Radio 통신을 위해서 IEEE 802.15.4를 기반으로 하고 있다.

그림 Ⅱ-1-1

그림에서 보는 것과 같이 IEEE802.15.4 규격을 사용하면서 Radio 통신

을 위해서 CC2420칩을 사용하고 있는 것을 볼 수 있다.CC2420칩을 이용한 여러 가지 Radio 부분의 설정을 CC2420RadioM.nc 파일을 통해 가능하다.

Page 96: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 93 -

1.1. 디렉토리 구조

TinyOS는 Cygwin의 'opt' directory안에 tinyos-1.x라는 이름을 가지고

위치하고 있다.

그림 Ⅱ-1-2 TinyOS 디렉토리 구조

tinyos-1.x 하부 디렉터리들을 좀더 살펴보겠다.

그림 Ⅱ-1-3 하부 디렉토리

- ‘apps' : 기본적인 TinyOS application 포함

- ‘contrib' : user(관련 회사나 단체)들이 제공한 application 포함.- ‘doc' : tutorial 및 여러 가지 문서들.- ‘tools' : utilities & java application- 'tos' : 하드웨어 관련 module 및 interface

Page 97: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 94 -

['tos' directory 내부]

그림 Ⅱ-1-4 tos 디렉터리 구조

- ‘interface' : TinyOS component를 위한interface모음

- ‘lib' : library 모음

- ‘platform' : platform(hardware)의존적인 component- 'sensorboards' : sensorboard(hardware)의존적인 component- 'system' : 기본적인 하드웨어 및 kernel 관련 component- 'types' : type 정의 (예, AM.h)

1.2. TinyOS Layers

그림 Ⅱ-1-5 TinyOS Layers

mote에 다운로드되는 하나의 application에는 TinyOS kernel이 그

application의 scheduler 역할과 hardware 초기화 역할을 수행하게 된

다.기존 embedded system과의 차이점이라면, OS가 탑재되고 OS 상에

서 여러 가지 application이 실행되는 반면, TinyOS는 하나의

application만을 동작 시킬 수 있다는 것이다.

Page 98: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 95 -

1.3. TinyOS Component Hierarchy

그림 Ⅱ-1-6 TinyOS Components의 Hierarchy

위의 그림은 TinyOS의 Component들의 Hierarchy들을 잘 보여주고

있다. 제일 왼쪽편에서 Radio 통신을 위한 component들의 계층도를

볼수 있다. UART는 PC와 serial 통신을 위한 component이다. ADC는

sensing 된 analogue값을 digital로 변환하여 준다.

2. TinyOS 설치하기

설치에 앞서 TinyOS 설치프로그램을 다운받는다. 본 문서에서 다운받는

TinyOS는 window용으로 하나의 설치파일로 되어 있으며, 설치하면

Cygwin, TinyOS, JDK 1.4 & JavaCOMM package, GraphViz 등이 설치된

다.

2.1. TinyOS 다운받기

http://www.tinyos.net/dist-1.1.0/tinyos/windows/ 주소로 들어가면 아

래와 같은 화면이 나온다.

그림 Ⅱ-2-1 다운로드 화면

여기서 tinyos-1.1.11-3is.exe(노란색 부분)을 다운 받는다.다운이 끝나면, 다운받은 폴더에 tinyos-1.1.11-3is.exe 파일이 생성된다.

Page 99: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 96 -

2.2. 설치과정

파일을 실행할 것인지 묻는 창이 뜨고, 실행을 클릭

그림 Ⅱ-2-2 파일 열기 window

설치 type 선택과 설치할 폴더를 지정해 준다. 기본적으로

complete type과 C: Program ₩ Files UCB₩ ₩로 정해져 있다.

선택하였다면 [다음]을 클릭

그림 Ⅱ-2-3 InstallShield

Java License에 대한 동의를 하고 설치 setting에 대한 확인을 하

면 설치가 시작된다. 설치에 필요한 파일들의 copy가 끝나면

Cygwin 설치를 위해 [Continue]를 클릭.

Page 100: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 97 -

Cygwin이 설치되고 다음과 같은 창이 뜨면서 설치가 끝났다.

그림 Ⅱ-2-4 설치 완료

platform에 따라서 따로 설치해야 하는 장치 Driver가 있을 수도

있으니 참고하기 바란다.

2.3. What's Being Installed?

그림 Ⅱ-2-5 Install 후 설치되는 요소들

처음 TinyOS를 설치하게 되면 위와 같은 것들이 설치된다. TinyOS가

리눅스환경에서 작동하기 때문에 Cygwin을 설치한다. 윈도우에서 리

눅스와 비슷한 환경을 제공해주는 프로그램이다.그리고 PC application들이 Java로 작성되어 있기 때문에 Java 1.4 JDK를 설치한다.

Page 101: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 98 -

3. TinyOS application 시작하기

3.1. application 설치하기

3.1.1. compile

그림 Ⅱ-3-1 Compile 과정

compile과정은 nesC로 구성된 application을 nesC compiler로

compile하면 app.c가 생성되고 이를 C compiler(avr, msp,...)로

compile하면 최종으로 원하는 object file(exe, hex, ...)이 생성되

게 된다. 이와 같은 일련의 과정이 make를 통해 이루어진다.

$> make <platform>이렇게 명령을 내리게 되면 application을 platform 별로 compile 해준다.

3.1.2. application을 mote에 다운로드

이제 compile된 image를 mote에 다운로드를 해야 한다.($> make install <platform>)

: 이 명령은 platform 별로 compile 한 후 mote에 다운로드 한다.

($> make reinstall <platform>)

: 이 명령은 이미 compile되어 있는 image를 mote에 다운로드 한다.

($> make install.<#> <platform>)

: 이 명령은 <#>자리에 숫자를 넣음으로써, mote에 ID를 부여해

준다. (reinstall도 동일.)

☞ <참고> - $> make install,1 <platform>

위의 명령은 1번 ID를 가진 mote로 application을 다운로드

하라는 명령이다.

3.1.3. application의 doc($> make docs <platform>)위 명령은 application을 platform 별로 document를 만든다. html 형태이며, 생성되는 위치는 make 할 때 첫 줄에 표시되어 있는

데 통상적으로 “C:₩Program Files UCB cygwin opt tinyos-1.x₩ ₩ ₩ ₩

₩doc₩nesdoc₩<platform>” 위치에 생성된다.

Page 102: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 99 -

3.1.4. TOS Message Packet StructureMote간의 정보는 TOS Message의 형태로 전달되게 된다. 아래 그림은 TOS Message의 구성을 보여준다.

그림 Ⅱ-3-2 TOS Message 구성

Payload의 길이, 순서 번호, PAN ID등이 나와 있고, Payload 부

분에 사용자가 원하는 정보가 들어가게 된다.

3.2. Blink applicationBlink는 일정시간 주기마다 led 하나가 켜졌다 꺼졌다를 반복하는 간

단한 application이다.우선 mote를 PC와 연결한다. (인터페이스에 따라서 usb 또는 serial)

Blink 디렉터리로 이동($>cd /opt/tinyos-1.x/apps/Blink)

Blink application을 compile하고 mote에 다운로드 ($>make install <platform>) 하면 아래와 같이 진행됨을 확인할 수 있다.

그림 Ⅱ-3-3 mote에 application을 다운로드 한 모습

Page 103: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 100 -

필자는 micaz를 사용했는데 따로 serial을 통하여 다운로드를 해

야 하기 때문에 compile만 수행하였다. 이와 같은 경우가 아니라

면 compile과 다운로드를 동시에 수행할 수 있다.

프로그램 다운로드가 끝나면 red led가 일정주기마다 깜박이는

것을 볼 수 있다.

3.3. CntToLedsAndRfm & RfmToLedsCntToLedsAndRfm은 숫자를 0~7까지 카운트하고 이를 3개의 led로

표시하며 동시에 RF로 전송해주는 application이다.RfmToLeds는 RF를 통해 들어오는 숫자를 3개의 led를 통해 표시하

는 application이다.따라서 하나의 mote에는 CntToLedsAndRfm을, 또 다른 mote에는

RfmToLeds를 다운로드한다.

하나의 mote를 PC와 연결하고 CntToLedsAndRfm application을

다운로드 한다.($>cd /opt/tinyos-1.x/apps/CntToLedsAndRfm)($>make install <platform>)

또 다른 mote에 RfmToLeds를 다운로드 한다.($>cd /opt/tinyos-1.x/apps/RfmToLeds)

($>make install <platform>)

실행.프로그램 다운로드가 모두 끝나면 CntLedsAndRfm가 다운로드 된

mote를 동작시키면 RfmToLeds도 동일하게 동작하고 CntLedsAndRfm이 동작을 중지하면 RfmToLeds도 중지하는 것을 볼 수 있다.

3.4. OscilloscopeRFOscilloscopeRF는 총 2개의 application으로 이루어진다. 하나는

TOSBase로 RF를 통해 Packet을 받아 PC로 넘겨주는 역할을 하게 되

며, 다른 하나인 OscilloscopeRF는 센서를 통해서 센싱을 하고 이 값

을 RF로 보내주는 역할을 하게 된다. 동작의 결과는 Java application을 통해 볼 수 있다.

OscilloscopeRF 다운로드 하기.Mote 하나를 PC와 연결한다.OscilloscopeRF 디렉터리로 이동($>cd /opt/tinyos-1.x/apps/OscilloscopeRF)OscilloscopeRF application을 compile하고 Mote에 다운로드

($>make install <platform>)

Page 104: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 101 -

TOSBase 다운로드 하기.이번에는 RF로 보낸 센싱값을 받아들여서 PC로 전달해 주는

TOSBase를 Mote에 다운로드한다.또 다른 하나의 Mote를 PC와 연결한다.TOSBase 디렉터리로 이동($>cd /opt/tinyos-1.x/apps/TOSBase)compile하고 다운로드한다.($>make install <platform>)

실행.프로그램의 다운로드가 모두 끝났다면, TOSBase를 PC와 연결하고, OscilloscopeRF가 설치된 Mote의 전원을 켠다. OscilloscopeRF가 다운로드된 Mote가 센싱된 정보를 RF를 통해서 보

낼 때마다, LED가 반짝거리는 걸 볼 수 있다면, 정상적으로 작동하

는 것이다. 마찬가지로 TOSBase가 다운로드된 Mote도 RF로 정보를

받을 때마다 LED가 깜빡거리는 걸 볼 수 있다.

3.5. PC application이제 위의 단계까지 완료가 되었지만, PC에서 직접 눈으로 확인할

수가 없어서 불편할 것이다. PC쪽의 application을 실행시켜 보자.

3.5.1. SerialForwarderSerialForwarder는 COM port로 받은 데이터를 IP로 전달해 주는 역할

과 서버 역할을 한다.java 디렉터리로 이동.($>cd /opt/tinyos-1.x/tools/java)

SerialForwarder을 실행.

($>java net.tinyos.sf.SerialForwarder -comm serial@COM7:<platform>&)

-comm은 자신의 PC에 맞는 serial port를 설정하게 해준다.

위의 예에선 7번 port로 TOSBase가 연결되어 있다고 가정하였다.

마지막의 &은 SerialForwarder application을 백그라운드 프로그램으로

실행시키라는 의미이다. 리눅스 명령이므로 자세한 설명은 생략.

Page 105: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 102 -

실행화면

그림 Ⅱ-3-4 SerialForwarder 실행화면

좌측에 로그가 보이는 데, 간단히 설명하면

‘serial@COM7:telos'에서 읽어오며, platform이 telos이고 client들은 port 9001번으로 접속하라고 나와 있다. 우측에는 서버

포트번호와 그 아래 연결된 COM 포트 번호가 있으며, 바로

아래 서버를 시작/중지 할 수 있는 버튼이 있다.그 아래에 ‘Pckts Read' 카운트 숫자가 보이며(이것이 올라가

지 않을 때는 데이터가 들어오고 있지 않다는 뜻이다.) ’Pckts Wrttn‘은 COM port로 데이터를 보내는 횟수를 표시하고, 그

밑 두 번째에 ’Num Clients'는 이 서버에 접속하는 client개수

이다. 그 밑에 도움말 버튼과 log 화면을 clear해주는 버튼과

프로그램 종료 버튼이 위치하고 있다.

3.5.2. oscilloscope Java applicationoscilloscope Java application은 SerialForwarder에서 제공해주는 port로 접속해서 data를 읽어오고, 이것을 graph로 표시해주는

application이다.java 디렉터리로 이동 ($>cd /opt/tinyos-1.x/tools/java)

application 실행 ($>java net.tinyos.oscope.oscilloscope)

Page 106: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 103 -

실행 화면

그림 Ⅱ-3-5 Oscilloscope 실행화면

위 그림과 같이 6개의 채널이 있을 수 있고 sensor의 종류에

따라 나타나지 않을 수도 있다. 만약 그래프들이 보이지 않을

경우 Scrolling 박스에 체크한다. 그래도 보이지 않을 경우

zoom 버튼을 이용해 간격을 조절하여 본다.

3.5.3. SimpleCmdSimpleCmd application은 위에서 사용했던 TOSBase application을 이

용하여 RF를 통해서 각각의 Mote에 간단한 명령을 던져서 명령에

따라 동작하게 하는 application이다.

3.5.3.1. Mote application 다운로드

TOSBase를 다운로드한다.

($>cd /opt/tinyos-1.x/apps/TOSBase)

($>make install <platform>)

SimpleCmd application을 다운로드한다.

($>cd /opt/tinyos-1.x/apps/SimpleCmd)

($>make install <platform>)

Page 107: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 104 -

3.5.3.2. PC application의 실행

명령을 내리기 위해선 두가지의 PC application이 필요하다. 하나

는 위에서 보았던 SerialForwarder이고, 다른 하나는 PC로부터

TOSBase를 통해 RF로 명령을 보내기 위한 BcastInject application

이다.

SerialForwarder을 실행.

($>java net.tinyos.sf.SerialForwarder -comm serial@COM7:<platform>&)

BcastInject를 실행.

($>java net.tinyos.tools.BcastInject <group_id> <command>)

<group_id>는 특별한 변경이 없었을 경우 0x7d가 default로 설

정되어 있다.

☞ <참고> - 그룹 ID 변경하기

($>cd /opt/tinyos-1.x/tools/make) 디렉터리 안에

“Makedefaults”라는 이름의 파일을 열어서, 첫 번째

줄의 “DEFAULT_LOCAL_GROUP” 상수의 값을 변경

해 줌으로써 가능하다.

<command>는 다음과 같은 4가지의 명령을 내릴 수 있다.

led_on - 노란색 LED를 켠다.

led_off - 노란색 LED를 끈다.

radio_louder - radio power를 증가시킨다.

radio_quieter - radio power를 감소시킨다.

3.6. SurgeSurge라는 application은 노드들 간에 능동적(ad-hoc, multi-hop)으로

네트워크를 구성하여 수집한 데이터를 PC로 전달하는 역할을 한다. 또한 TinyOS에서는 형성된 network topology를 보여주는 Java application을 제공하고 있다.

3.6.1. Surge 다운로드 하기.Surge 디렉터리로 이동 ($>cd /opt/tinyos-1.x/apps/Surge)

application을 compile하고 Mote에 다운로드 ($>make install.0 <platform>)

위 명령에서 install 뒤의 ‘0’이라는 숫자는 이 노드가 가지고 있는

구별되는 이름이 된다. 숫자를 ID로 가짐으로써 각각의 노드에 같

은 application이 올라가더라도 구별되어 질 수 있다.

Page 108: Mote 애플리케이션 개발 - Egloospds11.egloos.com/pds/200904/28/75/TinyOSnNesC.pdf · 2009. 4. 28. · - 1 - Ⅰ. NesC 1. Introduction 1.1. 개략적 설명 nesC는 TinyOS의

- 105 -

☞ <참고> - Mote ID 변경하는 또 다른 방법

</opt/tinyos-1.x/tos/system>의 디렉터리에 있는 “tos.h” 파

일을 열어서 TOS_LOCAL_ADDRESS으로 선언되어 있는 값을

변경시킴으로써 ID의 변경이 가능하다. (단, ‘0’번 Mote는 데

이터를 PC로 전달해주는 Sink node의 역할을 해야한다.)

위와 같은 방법으로 몇 개의 Mote를 번호를 달리해서 다운로드한다.

3.6.2. Surge Java application 실행.Sink node가 연결된 serial port를 export한다.

($>export MOTECOM=serial@COM1:57600)

위의 명령은 리눅스에서 환경변수를 설정하기 위한 명령인데,

MOTECOM이라는 이름의 환경변수를 정의해 줌으로써 Surge

application에서 Mote가 연결된 serial port를 알 수 있다.

application을 실행한다.

($>cd /opt/tinyos-1.x/tools/java)

($>java net.tinyos.surge.MainClass 0x7d)

※ 0x7d는 groupID의 변경이 없었을 경우 0x7d가 default로 설정

되어 있다. (groupID의 변경은 이전 페이지 참조.)

실행화면

그림 Ⅱ-3-6 Surge 실행화면

녹색의 원이 Mote를 나타내고 원위의 숫자가 MoteID를 나타낸다.

몇 개의 메시지를 받았는지가 표시되며, 메시지가 얼마나 정상적으

로 들어오는지에 대한 막대그래프가 아래 표시된다.