반응형

이번에는 몽고DB에서 데이터 조회를 해볼 것이다.

find는 RDB(MySQL과 같은...)에서의 SELECT 같은 역할을 한다.

find에도 옵션이 상당히 많기 때문에 여기서는 최소한의 기능을 알아볼 예정이다.

일단 find를 사용하기 전에 임시로 아래와 같이 데이타를 등록해놓았다.

 

db.temps.insert({name: '홍길동', age: 20, gender: '남'});
db.temps.insert({name: '최길동', age: 21, gender: '남'});
db.temps.insert({name: '김길동', age: 22, gender: '여'});
db.temps.insert({name: '윤길동', age: 23, gender: '여'});
db.temps.insert({name: '박길동', age: 24, gender: '남'});
db.temps.insert({name: '고길동', age: 25, gender: '남'});
db.temps.insert({name: '모길동', age: 26, gender: '여'});
db.temps.insert({name: '공길동', age: 27, gender: '남'});
db.temps.insert({name: '구길동', age: 28, gender: '남'});
db.temps.insert({name: '정길동', age: 29, gender: '남'});
db.temps.insert({name: '서길동', age: 30, gender: '남'});

 

위와 같이 데이타 삽입을 하고 아주 간단하게 다음과 같이 조회해볼 수 있다.

 

temps 컬렉션의 모든 항목이 표시된다.

MySQL로 예를 들면 'SELECT * FROM temps' 와 같은 명령과 동일하다고 보면 된다.

그리고 위의 경우 데이타가 깔끔하게 보여지지만 필드가 많아질 수록 제대로 정렬이 안되어서 보기 불편할 수가 있다. 그럴 때는 아래와 같이 find() 뒤에 .pretty()를 붙여주면 다음과 같이 데이터를 더 정리된 형태로 볼 수 있다.

 

이와 같이 잘 정렬되어 보여진다.

이제 다음으로는 기본적인 조건 옵션을 붙여보도록 할 것이다.(MySQL의 WHERE절과 같은)

 

도큐먼트 조회시 조건 걸기

이름이 '김길동'인 사람을 조회해보도록 하겠다.

db.temps.find({name: '김길동'});

위와 같이 단순하다. find() 명령안에 { } 중괄호로 원하는 조건을 감싸주기만 하면 된다.

 

만약 조건을 더 붙이고 싶다면 콤마(,)로 구분하여 뒤에 연결시켜주면 된다.

db.temps.find({name: '김길동', age: 22});

예를 들어, 위와 같은 명령이라면 name이 김길동이고 age가 22인 도큐먼트를 가져오게 된다.

 

도큐먼트 조회시 숫자를 비교하는 조건 걸기

조회할 경우 특정 데이타와 완전히 동일한지만 비교할 수도 있지만, 위의 데이타로 예를 들면 20~25세의 도큐먼트만

가져오는 등의 숫자 비교 조건을 걸 수도 있다.

 

MySQL의 경우 'SELECT * FROM temps WHERE age >= 20 AND age <= 25' 이런 형태로 단순하게 부호를 이용해서

비교가 가능하지만 MongoDB에서는 용법이 조금 다르다.

MongoDB에서는 다음과 같이 사용할 수 있다.

 

db.temps.find({age: {$gte: 20, $lte: 25}});

이와 같이 조건이 적용된 것을 확인할 수 있다.

위 명령을 분석해보면 $gte, $lte 라는 것들을 연산자(operator)라고 한다.

기본적으로 필드가 앞에 있고 필드 뒤에 연산자를 중괄호{}로 감싸서 사용하게 된다.

 

$gte는 greater than or equals이란 의미로 '같거나 크면'이라는 조건의 의미를 갖는다.

$lte는 less than or equals이란 의미로 '같거나 작으면'이라는 조건의 의미를 갖는다.

이외에 다양한 연산자가 존재한다.

 

$gt : 주어진 값보다 큰 경우

$lt : 주어진 값보다 작은 경우

$eq : 주어진 값과 일치하는 경우

$ne : 주어진 값과 일치하지 않는 경우

$in : 주어진 배열 안에 속하는 값

$nin : 주어진 배열 안에 속하지 않는 값

 

위는 비교 연산자이고, 그 외에 논리 연산자, 문자열 연산자 등 다양한 연산자가 따로 존재한다. 

그것은 각 연산자 사용시에 다시 알아보도록 하겠다.

 

도큐먼트 조회시 정렬하기

가져온 데이타들을 소팅할 수 있는 방법을 알아볼 것이다.

위에서 insert한 데이타들을 기준으로 age 필드의 데이타를 내림차순으로 정렬해보려 한다.

db.temps.find().sort({age: -1});

아주 간단하게 정렬이 되었다.

find()뒤에 sort()명령을 붙여줄 수 있고 중괄호 안에 원하는 정렬 기준을 지정해주면 된다.

age: -1는 age 필드를 내림차순으로 정렬한다는 의미이다.

-1은 내림차순이지만 오름차순으로 정렬하고 싶다면 {age: 1}과 같은 형태로 사용하면 된다.

이 때 .sort() 명령 뒤에 .pretty()를 붙여서 더 정리된 형태로 표시할 수도 있다.

 

도큐먼트 조회시 특정 개수만 표시하기, 특정 위치의 데이타 가져오기

데이타 수량이 너무 많거나 페이지네이션 구현 등을 위해 특정 개수만 표시하고 싶을 때가 있다.

이 때는 .limit()이라는 명령을 사용할 수 있다.

 

db.temps.find().limit(1);

이와 같이 가져올 데이타의 상위 1개의 데이타만 가져오게 된다.

페이지네이션 구현을 위해 많이 사용하게 된다. 페이지네이션 구현시에는 특정 개수만 가져오는 것도 필요하지만,

특정 위치의 도큐먼트를 가져오는 것도 필요하다.

이 때는 .skip()이라는 명령을 사용할 수 있다.

 

db.temps.find().skip(2).limit(2);

조회할 데이타의 2개 뒤의 데이타들 중 2개만 가져오는 명령이다.

이를 통해서 페이지네이션을 구현할 수 있다.

 

여기까지 find의 기본적인 내용들을 다루어보았고, 다음에는 여기서 설명하지 않았던 다른 연산자들에 대해서도

알아보려 한다.

반응형
반응형
defaultConfig {
    // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
    applicationId "com.test.abcd"
    minSdkVersion flutter.minSdkVersion
    targetSdkVersion flutter.targetSdkVersion
    versionCode flutterVersionCode.toInteger()
    versionName flutterVersionName
}

 

위 코드에서 보다시피 각 속성들이 참조로만 지정되어 있다.

Flutter 2.8 전에는 직접 입력되어져 있었으나 2.8 이후부터는 위처럼 참조 형태로 바뀌게 되었다.

 

기본 값은 무엇으로 되어있는지 확인하기 위해 검색을 해보았다.

위와 같이 검색해보았으나, 참조되어있는 부분 빼고는 원 출처가 어디인지 나오지 않았다.

그 이유는 프로젝트 내에 해당 정보가 있지 않고 플러터 SDK 경로안의 gradle 파일을 참조하기 때문이다.

 

해당 파일의 위치는 아래와 같다.

플러터 경로가 C:\flutter라고 가정할 경우 C:\flutter\packages\flutter_tools\gradle\flutter.gradle 파일안에 해당 정보가 있다.

 

파일을 열어보면 위와 같이 선언되어 있는 것을 확인할 수 있다.

굳이 왜 저렇게 했을까 싶다. 플러터 버전을 업그레이드하면서 자기 마음대로 해당 Sdk버전을 변경해버릴 수 있기 때문에, 나의 경우는 위 선언 내용을 그대로 사용하지 않는다.

 

applicationId "com.test.abcd"
minSdkVersion 16
targetSdkVersion 31
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName

위와 같이 숫자를 직접 대입해서 쓰는 것도 간편하고 나쁘지 않은 방법이라고 본다. (기존의 방법)

하지만 나는 현재 아래처럼 사용하고 있다.

def flutterCompileSdkVersion = localProperties.getProperty('flutter.flutterCompileSdkVersion')
if (flutterCompileSdkVersion == null) {
    flutterCompileSdkVersion = '31'
}

def flutterMinSdkVersion = localProperties.getProperty('flutter.flutterMinSdkVersion')
if (flutterMinSdkVersion == null) {
    flutterMinSdkVersion = '16'
}

def flutterTargetSdkVersion = localProperties.getProperty('flutter.flutterTargetSdkVersion')
if (flutterTargetSdkVersion == null) {
    flutterTargetSdkVersion = '31'
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
    compileSdkVersion flutterCompileSdkVersion.toInteger()

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
        applicationId "com.test.abcd"
        minSdkVersion flutterMinSdkVersion.toInteger()
        targetSdkVersion flutterTargetSdkVersion.toInteger()
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.debug
        }
    }
}

flutterVersionCode, flutterVersionName은 local.properties를 참조하는 형태로 되어있었는데,

compileSdkVersion, minSdkVersion, targetSdkVersion 또한 그렇게 참조하는 형태로 바꿔버리는 것이다.

sdk.dir=C:\\Users\\Master\\AppData\\Local\\Android\\sdk
flutter.sdk=C:\\flutter
flutter.buildMode=debug
flutter.versionName=1.0.0
flutter.versionCode=1
flutter.flutterCompileSdkVersion=31
flutter.flutterMinSdkVersion=16
flutter.flutterTargetSdkVersion=31

그리고 local.properties 파일을 위와 같이 수정해주면 좀 더 깔끔한 형태로 sdk 버전을 관리할 수 있게 된다.

 

반응형
반응형

AWS SSH를 접속하기 위해 보통 Putty라는 SSH 접속 프로그램을 사용하게 되는데,

AWS에서 SSH 접속을 위해 제공해주는 키 페어 파일은 확장명이 pem파일인데 Putty에서는 ppk 파일을 요구한다.

즉, pem파일을 ppk파일로 변환하는 과정을 거쳐야 한다.

 

변환하기 위해서는 puttygen.exe 파일을 다운로드 받고 실행한다.

(https://www.puttygen.com/ 에서 다운로드 가능하다.)

위와 같이 프로그램이 실행되었으며, 위의 Load 버튼을 클릭한다.

먼저 pem 파일을 불러와야 하므로 확장자를 All Files(*.*) 로 변경해준 뒤 키 페어 파일(.pem)을 불러온다.

성공적으로 불러왔다는 메세지가 뜬다.

'Save private key' 버튼을 클릭한다.

위의 경고창에서는 '예' 버튼을 클릭한다. 

그리고 원하는 파일명의 ppk 파일로 저장하기만 하면 된다.

최종적으로 ppk 파일이 생성되며 해당 파일을 Putty에 지정하여 사용하면 된다.

 

참고로 Putty에서 ppk 파일 설정하는 영역은 아래와 같다.

Connection->SSH->Auth의 Private key file for authentication 부분을 생성한 키페어 파일로 설정해주고

접속하면 정상적으로 접속될 것이다.

반응형
반응형

 

플러터로 앱 개발하던 도중 잘만 실행 되다가 어느 순간 갑자기 아래와 같은 오류를 발견하였다.

 

위와 같이 devtools-2.9.2\build 폴더를 찾을 수 없다는 오류였으며,

저렇게 오류가 발생하여도 실행하는데는 문제가 없었지만 무언가 찝찝했다.

 

실제로 저 경로에 들어가보아도 devtools-2.9.2 폴더 안에 build 폴더는 존재하지 않았다.

무언가의 영향을 받아서 build를 생성하지 못한 것 같다.

 

먼저 위 경로 중 pub.dartlang.org 폴더까지 들어가보면 아래와 같이 많은 버전의 devtools가 깔려있다.

(devtools-2.9.2 폴더는 어짜피 안되니까 삭제해버렸다.)

 

해결방법은 단순하게 사용할 버전을 바꾸어주면 된다.

 

dart pub global activate devtools -v 2.8.0

 

위 명령어를 실행하게 되면 기존 활성화되어 있던 devtools-2.9.2 버전에서 devtools-2.8.0 버전으로 활성화 버전이 변경된다.

2.8.0 버전 안에는 정상적으로 build폴더도 존재하고 문제가 없으므로 위의 문제는 간단히 해결된다.

이렇게 하지 않고 devtools-2.9.2 버전을 재설치하는 방법도 있겠으나,

일단 급하게 해결이 우선이었고 버전을 바꾼다하여도 문제될 것은 없었기 때문에 위와 같은 방법으로 해결하였다.

반응형
반응형

이번에는 몽고DB에서 데이타를 삭제하는 방법을 알아볼 것이다.

remove는 단일 혹은 다수 삭제가 모두 가능하고 deleteOne은 단일 삭제만 deleteMany는 다수 삭제만

가능하다. MongoDB 3.2버전부터 remove 대신 deleteOne, deleteMany로 대체되었다.

 

1. 삭제(remove)

MySQL에서 'DELETE FROM' 과 매칭되는 명령이다.

insert 혹은 update명령과 비슷한 형태로 사용하게 되는데 단순히 삭제할 조건만 넣어주면 된다.

먼저 위와 같이 데이터를 넣어놓고, 이름이 "최길동"인 사용자를 삭제해보도록 하겠다.

db.users.remove({name: "최길동"});

실행 결과

정상적으로 삭제되었으며, 조건에 따라 단일 삭제가 될 수도 있고 다수 삭제가 될 수도 있다.

예를 들어, 나이가 27살 이상인 사용자를 삭제하고 싶을 경우 다음 명령을 사용한다.

db.users.remove({age: {$gte: 27}});

($gte 연산자는 'greater than or equal'의 약자로 '같거나 크면'의 의미를 갖는다.

 여러 연산자에 대해서는 다음 포스팅에서 다뤄볼 예정이다.)

이와 같이 3개가 삭제되었다는 메세지가 나오며, 조회해보니 정상적으로 삭제된 것을 확인할 수 있다.

remove()의 사용법을 알아보았다. 하지만 몽고3.2 이후에는 deleteOne, deleteMany로 대체되었으니,

remove의 사용은 자제하는 것이 좋다.

 

2. 단일 삭제(deleteOne)

지난번 포스팅에서 설명했던 insertOne, insertMany, updateOne, updateMany와 같은 형태로 용법이 비슷하고,

바로 위에 설명한 remove() 용법이 단일, 다수 삭제 기능으로 분리된 것 뿐이기에 간단하게 설명하고 넘어갈 것이다.

 

db.users.deleteOne({name: "최길동"});

'최길동'이라는 사용자 도큐먼트를 삭제하는 명령이다.

(최길동이라는 사용자가 두명이면 가장 처음의 '최길동' 사용자 도큐먼트만 삭제된다.)

 

db.users.deleteOne({});

처음 도큐먼트 한개를 삭제하는 명령이다.

 

db.users.deleteOne({age: {$gte: 27}});

age 필드가 27 이상의 도큐먼트 중 처음 도큐먼트 한개를 삭제하는 명령이다.

 

3. 다수 삭제(deleteMany)

조건에 해당하는 데이타를 모두 삭제할 때 사용한다.

 

db.users.deleteMany({name: "최길동"});

'최길동'이라는 사용자 도큐먼트를 모두 삭제한다.

 

db.users.deleteMany({});

users 컬렉션의 모든 도큐먼트를 삭제하는 명령이다.

 

db.users.deleteMany({age: {$gte: 27}});

age 필드가 27 이상의 도큐먼트를 모두 삭제한다.

 

 

반응형
반응형

이번에는 몽고DB에서 데이타를 수정하는 방법을 알아본다.

update 또한 insert와 마찬가지로 함수 형태가 크게 세개로 구성된다.

update 명령은 단일 도큐먼트 혹은 다수의 도큐먼트를 수정할 수 있고 updateOne은 단일 도큐먼트만,

updateMany는 다수의 도큐먼트만 수정 가능하다.

MongoDB 3.2버전 이후부터는 update() 함수 대신 updateOne()과 updateMany() 함수를 사용하기를 권장한다.

 

1. 수정(update)

데이타를 수정할 때 사용하는 명령이며, MSQL의 update와 매칭되는 명령이다.

insert 명령과 마찬가지로 JSON 형태로 데이터를 수정할 수 있다.

 

위와 같은 데이터가 있다고 가정해보자.

name이 '홍길동'인 age의 값 20을 23으로 변경하고자 하면 아래와 같이 명령을 실행할 수 있다.

db.users.update({name: '홍길동'}, {$set:{age: 23}});

 

위와 같이 정상적으로 입력된 것을 확인할 수 있다.

데이터를 조회해보니 age값이 20에서 23으로 변경되었다.

MongoDB에서 update명령을 사용하여 특정 필드의 값을 변경할 때는 $set 연산자를 사용하여야 한다.

만약 사용하지 않는다면 어떻게 될까?

 

$set 연산자를 제거하고 나이를 25살로 변경하여 입력해보았더니 위와 같은 결과가 나왔다.

$set 연산자를 사용하지 않을 경우 입력한 데이터 자체를 통째로 덮어버리게 된다.

즉, 기존에 존재하던 name 필드까지 사라져버리는 것이다.

그렇기에 update 명령 사용시에는 반드시 주의해야 한다. $set 사용하는 것을 실수해서 

다른 필드를 날려버리는 경우가 종종 발생할 수 있다.

 

update 명령은 단일 도큐먼트도 수정 가능하지만 다수의 도큐먼트도 수정 가능하다.

다수의 도큐먼트 수정시에는 multi 옵션을 사용하여 수정할 수 있다.

먼저 테스트를 위해 위와 같이 데이터를 준비하였다.

위 두 데이터 전체의 나이를 25로 변경해보도록 하겠다.

db.users.update({}, {$set: {age: 25}}, {multi: true});

앞의 조건 {}은 전체 필드를 수정하겠다는 의미이다.

조회해보면 다음과 같은 결과가 나온다.

age 필드가 모두 정상적으로 25로 변경되었다.

만약 multi 옵션을 사용하지 않는다면 기본적으로 단일 도큐먼트 수정이 되기 때문에 가장 처음 데이터만 변경이 된다.

 

2. 단일 데이터 수정(updateOne)

update와 사용법은 동일하다. 단지, 단일 도큐먼트만 수정될 뿐이다.

db.users.updateOne({name: '홍길동'}, {$set: {age: 25}});

이와 같이 명령을 실행하면 name 필드가 '홍길동'인 age 값을 25로 변경한다.

완전히 동일하다.

 

3. 다수 데이터 수정(updateMany)

update의 multi 옵션을 자동으로 사용해준 명령이라고 보면 된다.

위의 두개 도큐먼트에 대한 age를 모두 27로 변경해보겠다.

db.users.updateMany({}, {$set: {age: 27}});

multi 옵션 없이 조건에 해당하는 모든 필드가 알아서 수정이 된다.

updateOne과 updateMany은 update 명령에 비해 좀 더 명시적으로 업데이트되었다고 보면 될 것 같다.

 

반응형
반응형

 

몽고DB에서 데이터 삽입하는 방법에 대해 알아보도록 할 것이다.

데이터를 삽입하는 방식에는 크게 세가지가 있다.

insert 명령, insertOne명령, insertMany명령인데, insert의 경우 단일 도큐먼트 혹은 다수 도큐먼트 모두 삽입이

가능하며, insertOne의 경우 단일 도큐먼트만, insertMany는 다수 도큐먼트만 삽입할 수 있다.

 

1. 삽입(insert)

DB에 데이터를 삽입할 때 사용하는 명령어이다. MySQL의 insert와 매칭되는 명령인데,

MySQL의 경우 각 필드에 데이터를 입력시켜주는 반면 몽고DB에서는 데이터를 도큐먼트 단위로 입력시켜주게 되며,

도큐먼트는 JSON 형태의 데이터로 구성된다.

 

users 컬렉션에 아래와 같이 데이터를 입력시켜볼 수 있다.

db.users.insert({

    name: '홍길동',

    age: 20,

});

위와 같이 입력이 완료되었고, find() 명령을 통해 입력된 데이타를 확인할 수 있다.

db.users.find();

위는 users 컬렉션에 있는 모든 도큐먼트를 조회한 결과이다.

insert() 명령으로 넣은 JSON 데이타가 그대로 들어가있다.

그런데 _id라는 필드는 입력하지 않았는데 자동으로 생겨버렸다.

이 필드는 MySQL에서 Primary key와 같은 역할을 하는 필드라고 보면 된다.

중복을 허용하지 않는 고유의 값이며, 기본적으로 인덱스가 걸려있기 때문에 해당 필드로 조회시 검색 성능은 보장된다. 

 

insert를 이용하여 다수의 데이타를 한번 삽입해보도록 하겠다.

다수의 데이타 삽입시에는 배열을 사용한다.

db.users.insert([
{
    name: '최길동',
    age: 21,
},
{
    name: '박길동',
    age: 22,
},
]);

위와 같이 대괄호 []를 통해 다수의 데이터를 삽입할 수 있다.

정상적으로 다수의 데이타가 입력된 것을 확인할 수 있다.

 

2. 단일 데이터 삽입(insertOne)

insertOne은 단일 도큐먼트를 삽입하는 명령이다.

몽고 3.2버전 이후에 추가된 명령이며, insert를 대체하기 위해 만들어졌다고 한다.

insert()명령과 사용법은 동일하며, 배열 형태로 다수의 데이터를 삽입하는 것은 insertMany명령을 이용해야 한다.

db.users.insertOne({
    name: '홍길동',
    age: 20,
});

아래와 같이 조회하면 정상적으로 데이타가 삽입된 것을 확인할 수 있다.

 

3. 다수 데이터 삽입(insertMany)

insertMany은 다수의 도큐먼트를 한번에 삽입하는 명령이다.

insertOne과 같이 몽고 3.2버전 이후에 추가된 명령이다.

다수 데이터 삽입이므로 배열 형태로 삽입하며 아래와 같이 명령을 사용할 수 있다.

db.users.insertMany([
{
    name: '최길동',
    age: 21,
},
{
    name: '박길동',
    age: 22,
}

]);

정상적으로 삽입이 완료되었다.

 

몽고3.2버전 이후에는 insertOne(), insertMany() 명령을 사용을 권장하므로, insert() 명령 사용은 자제하는 것이 좋을 것으로 보인다.

반응형
반응형

 

여기서는 STM32CubeMX 5.6.1 버전을 기준으로 할 것이다.

(어짜피 버전이 달라도 UI 또는 편의성 정도의 차이일 것이기에 크게 문제될 것은 없을 것 같다.)

 

MCU는 STM32103C8Tx(LQFP 48) 기준으로 작업할 것이다.

이것 또한 MCU가 다르다고 크게 다를 것은 없다.

 

여기서 MCU의 10번 핀을 PWM 핀으로 사용해볼 것이다.

데이타시트에 보면 확인할 수 있듯이 LQFP48에 대한 10번은 TIM2_CH1 기능을 사용할 수 있도록 되어있다.

타이머 채널들은 모두 PWM이 사용 가능하다고 보면 된다.

이제 STM32CubeMX를 실행하여 MCU의 기본적인 세팅을 해준다.(크리스탈, 디버거 설정 등)

나의 경우 8MHZ 크리스탈이 달려있고 ST-LINK를 사용할 것이기에 RCC_OSC 설정을 해주었고,

PA13, PA14쪽의 SWCLK와 SWDIO 설정도 같이 해주었다.

 

이제 10번 핀(PA0)을 PWM 핀으로 설정해보도록 하겠다 해당 핀을 클릭하면 아래와 같은 메뉴가 뜬다.

여기서 TIM2_CH1을 클릭한다.

이와 같이 노란색으로 바뀌었으며 이 상태는 핀을 사용하겠다고는 했지만 디테일한 세팅이 되어있지 않은 것이다.

위와 같이 TIM2에서 Channel1을 선택한 후 PWM Generation CH1을 선택해준다.

 

이와 같이 설정이 완료된 상태로 변경되었다.

이제 가장 중요한 설정을 할 차례다.

원하는 PWM 주파수를 만들기 위해 Prescaler와 주기 값 등을 지정해주어야 한다.

아래 파라미터 설정을 보고 설명하도록 하겠다.

 

가장 중요한 세가지 값이 있다.

Prescaler와 Counter Period로 PWM 주기를 지정하고 Pulse 값으로 Duty를 지정할 수 있다.

먼저 주기를 설정하기 위해서는 나의 타이머가 얼마의 클럭으로 돌아가는가를 알아야 한다.

 

STM32CubeMX의 Clock Configuration 탭을 확인하면 다음과 같이 클럭을 확인할 수 있다.

위와 같이 APB1 Timer clocks과 APB2 timer clocks를 확인하면 된다.

자신이 어떤 타이머를 사용할지에 따라 위, 아래의 클럭 중 어떤 것을 사용하는지가 결정된다.

이 또한 아래의 데이타시트 Clock tree에서 확인할 수 있다.

이는 MCU마다 차이가 있을 수 있으므로 반드시 Datasheet를 확인해야 한다.

TIM1은 APB2를 쓰고 TIM2,3,4는 APB1을 쓴다. 

나는 여기서 TIM2_CH1을 쓸 것이니 APB1 클럭만 신경쓰면 된다.

 

200hz의 50% Duty의 PWM 파형을 만들어보도록 하겠다.

 

Prescaler = 799

Counter Period = 49

PWM Pulse = 99 

이렇게 지정하였다.

여기서 각 값들이 800, 50, 100도 아니고 1씩 떨어져있는 것을 볼 수 있는데,

이것은 각 값들이 0부터 시작하므로 실제 적용할 값에서 -1을 해주어야 한다.

이제 위 값들이 어떻게 계산되는지 알아보도록 하겠다.

 

APB Clock 이 8Mhz였으므로, 먼저 해당 클럭이 프리스케일링된다.

8,000,000 / 800 (Prescaler) = 10,000 Hz(10Khz)

 

여기서 Counter Period에 의해 200Hz로 조정된다.

10,000 Hz / 50 (Counter Priod) = 200Hz

 

이제 200Hz 주기인 PWM의 Duty를 50% 로 지정한다.

200 / 100 (Pulse) = 2 (50% Duty)

 

CubeMX 에서의 모든 설정은 마쳤다고 볼 수 있다.

여기서 모두 값을 지정해주었지만, 얼마든지 코드 상에서 값을 수정할 수도 있다.

이제 우측 상단의 GENERATE CODE를 눌러서 코드를 생성하고 해당 코드를 불러오도록 한다.

PWM 파형을 동작시키는 코드를 작성해보겠다.

 

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); 한줄만 추가해주면 되므로 매우 간단하다. 

코드를 보면 유추할 수 있듯이 TIM2의 CH1의 PWM을 동작하도록 하는 코드이다.

이제 컴파일 후에 실행하여 결과를 확인해보면 정상적으로 PWM 파형이 발생하고 있을 것이다.

(참고로 Unknown Target 과 같은 컴파일 오류가 발생한다면 프로젝트 옵션에서 Target MCU 설정을 해주어야 한다.)

반응형
반응형

[2021-08-20T07:53:00,865][INFO ][org.logstash.beats.BeatsHandler][main][85cdcfd8ac49789653328fffd54627260e267cb5c24953396322a6d4da0521f2] [local: 172.11.0.3:5000, remote: 142.128.153.77:53930] Handling exception: io.netty.handler.codec.DecoderException: org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69 (caused by: org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69)
[2021-08-20T07:53:00,865][WARN ][io.netty.channel.DefaultChannelPipeline][main][85cdcfd8ac49789653328fffd54627260e267cb5c24953396322a6d4da0521f2] An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.DecoderException: org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:477) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:404) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelInputClosed(ByteToMessageDecoder.java:371) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.channelInactive(ByteToMessageDecoder.java:354) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeChannelInactive(AbstractChannelHandlerContext.java:262) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.channel.AbstractChannelHandlerContext.access$300(AbstractChannelHandlerContext.java:61) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.channel.AbstractChannelHandlerContext$4.run(AbstractChannelHandlerContext.java:253) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.concurrent.DefaultEventExecutor.run(DefaultEventExecutor.java:66) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) [netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) [netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) [netty-all-4.1.65.Final.jar:4.1.65.Final]
        at java.lang.Thread.run(Thread.java:829) [?:?]
Caused by: org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69
        at org.logstash.beats.Protocol.version(Protocol.java:22) ~[logstash-input-beats-6.1.6.jar:?]
        at org.logstash.beats.BeatsParser.decode(BeatsParser.java:62) ~[logstash-input-beats-6.1.6.jar:?]
        at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:507) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:446) ~[netty-all-4.1.65.Final.jar:4.1.65.Final]
        ... 11 more
------------------------------------------------------------------------------------------------------------------------------

 

현재 로깅 시스템 구축을 위해 FileBeat로 LogStash에 로깅 데이타를 Shipping 하는 방법으로 구성해놓았다.

그런데 로그스태시쪽에서 위와 같은 오류를 뿜어내기 시작했다. 

org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 69 이런 오류와 동시에

org.logstash.beats.InvalidFrameProtocolException: Invalid version of beats protocol: 71 오류가 발생하기도 한다.

이런 경우 오류 내용에서도 알 수 있듯이 버전 호환이 잘 되지 않는 것이다.

위 문제 발생시 내가 쓰고 있는 ELK 스택의 버전은 7.14.0이었고, FileBeat의 버전은 7.4.2였는데 버전 차이로 인한

프로토콜의 차이가 발생하여 위와 같은 에러가 발생하는 것으로 추측했다.

FileBeat 버전도 최신버전인 7.14.0으로 업그레이드하고 테스트해보니 해당 문제는 사라졌다.

 

반응형
반응형

 

플러터가 2.0이 발표되면서 프로젝트 관리에 이슈가 생겼다.

안정적인 프로젝트 운영을 위해 2.0으로 업그레이드하는 것이 마땅하나,

이미 1.0으로 개발을 완료한 프로젝트를 2.0으로 업그레이드하기란 그렇게 간단하지 않기 때문이다.

게다가 플러터 1.0으로 개발된 프로젝트가 많은 상황에 플러터 버전을 2.0으로 업그레이드하는 순간

모든 프로젝트들을 플러터 2.0에 맞게 마이그레이션해주어야 하는데 보통 일이 아니다.

 

나의 경우도 계속 2.0으로 업그레이드하는 것을 미루다가 플러터의 특정한 패키지 버그 해결을 위해

플러터 2.0 업그레이드가 불가피해져서 업그레이드하기로 결정했는데 다 바꾸기엔 무리가 있으니,

해당 프로젝트 하나만 플러터 2.0으로 마이그레이션 하기로 결정하였다.

이 때 사용하는 것이 FVM(Flutter Version Management)이라는 것이다.

이 FVM을 사용하게 되면 프로젝트별로 원하는 버전을 쉽게 변경해서 사용할 수 있다.

A라는 프로젝트는 플러터 1.0, B라는 프로젝트는 플러터 2.0 이런 식으로 선택적으로 사용할 수 있게 된다.

또 B라는 프로젝트가 플러터 2.0으로 사용하고 있더라도 fvm으로 1.0으로 변경해서 사용할 수도 있다.

 

이제 FVM 사용하는 방법을 설명할 것이다.

 

1. 먼저 FVM을 활성화한다.

터미널(혹은 명령프롬프트)을 열고 아래 명령을 실행한다.

dart pub global activate fvm

(위 명령이 되지 않는다면 pub global activate fcm 이 명령으로 실행해보도록 한다.)

※ 혹시나 무언가 잘못되어서 되돌려야 하는 경우라면 pub global deactivate fvm 명령을 사용하면 된다.

 

2. 환경변수 path를 실행해준다.

윈도우10 기준으로 보통 'C:\Users\사용자이름\AppData\Local\Pub\Cache\bin' 이 경로를 설정해주면 될 것이다.

MAC일 경우는 export PATH="$PATH":"/Users/사용자이름/.pub-cache/bin" 과 같은 형태로 지정해주면 된다.

(사용자이름은 본인의 PC에 설정된 사용자이름으로 치환해서 사용한다.)

그리고 MAC에서는 터미널 실행시마다 적용될 수 있게 ~/.bashprofile 파일 내에도 위 패스에 대한 것을 적용해주도록 한다.

(zsh을 사용한다면 ~/.zshrc를 수정해야 하며 없으면 생성하여 내용을 추가하면 된다.)

 

※ 혹시나 Dart 경로(나의 경우는 C:\flutter\bin\cache\dart-sdk\bin)도 환경변수에 지정되어 있지 않다면 지정해준다.

   Dart 경로를 추가하는 이유는 fvm 활성화 후 실행할 fvm 명령이 내부적으로 dart의 pub 명령어를 실행하기 때문이다.

 

경로 지정을 해주지 않을 경우 아래와 같은 오류가 발생할 수 있으므로 참고하도록 한다.

-------------------------------------------------------------------
Installed executable fvm.
Warning: Pub installs executables into C:\Users\Master\AppData\Local\Pub\Cache\bin, which is not on your path.
You can fix that by adding that directory to your system's "Path" environment variable.
A web search for "configure windows path" will show you how.
Activated fvm 1.3.7.
-------------------------------------------------------------------

 

3. fvm help 명령을 실행해서 설치가 정상적으로 되었는지 확인한다.

이런 형태로 명령어 목록이 뜬다면 정상적으로 설치된 것이다.

 

4. 사용할 플러터 버전을 설치한다.

fvm을 사용할 경우 fvm 내부에서 플러터 버전을 별도로 관리하므로,

기존에 쓰던 플러터 버전은 아무 의미가 없다.

나의 경우 기존 플러터 버전을 1.22.5를 사용했었고, 이 버전과 최신버전 두가지 버전으로 관리할 것이다.

아래 두 명령을 실행하여 플러터 두가지 버전을 모두 설치해준다.

fvm install 1.22.5

fvm install stable (이 명령은 최신의 안정적인 플러터 버전을 설치해준다.)

 

5. 설치된 버전을 확인해본다.

fvm list 명령을 사용하여 아래와 같이 설치된 버전 목록을 확인할 수 있으며,

해당 플러터 버전들이 어느 경로에서 관리되고 있는지도 확인할 수 있다.

 

6. 내가 원하는 프로젝트에 원하는 플러터 버전을 적용한다.

터미널에서 플러터 버전을 적용할 프로젝트 경로로 이동한다.

그리고 fvm use라는 명령어를 사용하여 원하는 버전으로 변경할 수 있다.

최신버전의 플러터 버전을 사용하기 위해 아래 명령을 실행한다.

fvm use stable (다른 버전을 사용하려면 fvm use 다른버전을 입력하면 된다.)

위와 같이 표시된다면 정상적으로 버전이 변경된 것이다.

 

※ 오류 대응 방법

(1) Could not install stable 에러 발생

만약 위와 같은 에러가 뜬다면, 명령프롬프트에 관리자 권한이 필요한 것일 수 있다.

윈도우10이라면 PowerShell을 관리자 권한으로 실행해서 사용하면 정상적으로 동작할 것이다.

 

(2) FVM 업그레이드를 요하는 문구

___________________________________________________
FVM Update Available 1.3.7 → 2.2.2
Changelog: https://pub.dev/packages/fvm/changelog
Run pub global activate fvm to update
___________________________________________________

 

위와 같이 뜨는 경우는 flutter upgrade를 통해 기존에 사용하던 플러터를 최신버전으로 업그레이드하면 된다.

그리고 다시 fvm을 활성화하는 명령을 실행해주면 정상적으로 동작하게 된다.

 

7. 해당 프로젝트에 원하는 버전이 정상적으로 적용되었는지 확인한다.

fvm flutter --version 명령을 실행한다.

위와 같이 정상적으로 stable 버전이 선택된 것을 확인할 수 있다.

flutter --version 명령을 실행할 경우 fvm 외부의 플러터 버전이 보여지므로 착오하지 않도록 한다.

 

8. 안드로이드 스튜디오에서 fvm에서 설정한 버전을 사용하도록 변경한다.

아무리 fvm use로 버전 변경을 하더라도 해당 프로젝트에서 fvm 플러터 버전을 참조하도록 경로 수정을 하지 않는다면 의미가 없다.

안드로이드 스튜디오를 실행하고 File -> Setting -> Languages & Frameworks -> Flutter -> Flutter SDK Path를 프로젝트 루트에 생성된 .fvm/flutter_sdk 의 경로를 지정해주면 된다.

위와 같이 적용하고 OK 버튼을 눌러주면 모든 설정이 끝난다.

이제 해당 프로젝트를 빌드하거나 디버깅을 하게 되면 적용된 플러터 버전으로 실행할 수 있을 것이다.

다만, 적용한 플러터 버전에 맞게 프로젝트 내의 소스 코드나 패키지도 마이그레이션해야 정상적으로 실행될 것이다.

그리고 터미널에서 빌드시에 flutter build apk, flutter build ios 등의 명령들을 사용하게 되는데, 

이 때에도 fvm flutter build apk, fvm flutter build ios와 같이 적용 중인 플러터 버전으로 빌드할 수 있는 명령어로 사용해야한다.

반응형

+ Recent posts