2017년 6월 9일 금요일

io.adafruit.com 활용기

1. MQTT Broker를 알게되다.

 Arduino와 각종 센서들을 이용하여 데이터의 취득이 가능해지면서 데이터의 손쉬운 열람이나 축적이 필요하게 되었다. 웹서버, RDB등을 이용하여 웹으로 정보를 표시할 수 있기는 하지만 최대한 손쉽게 하기 위하여 방법을 모색하다가 MQTT Broker라는 것을 알게 되었다.

MQTT Broker에 대한 이미지 검색결과
[ MQTT Broker가 하는일 ]
마침 내가 하려고 하는 것에 부합되는 내용이라 이를 NAS에 설치하여 사용하려 하였으나, MQTT Broker Site가 있다는 것을 알게 되었고 이를 이용하기로 결정하였다.


2. io.adafruit.com을 선택하다.

 MQTT Broker Site는 여러 곳이 있다. 특별히 많은 곳을 살펴보지는 않았지만, 무료 이면서 이미지 처리가 가능한 곳을 찾다가 발견한 곳이 바로 io.adafruit.com 이었다.
 카메라에서 촬영한 이미지를 모바일로 어디에서나 볼 수 있도록 만드는 작업은 여러모로 껄끄럽다.  (이미 웹서버를 따로 사용하지 않기로 마음먹은 순간 불가능해 보였다). 하지만 io.adafruit.com 에서 처리가 가능했기 때문에 바로 선택하였다. 이 사이트는 초기에는 이미지 정보를 수집하여 GIF 애니 메이션으로 만들어 주었다. 즉 동영상 수준은 아니더라도 GIF를 통하여 움직이는 모습을 만들수 있었던 것이었다. 안타깝게도 사용자가 늘어나자, 관련 기능은 삭제 되었고 현재는 마지막 이미지만을 표시한다. Trigger기능도 제공하고  Arduino용 라이브러리 또한 제공하고 있어서 편리했다.

 참고로 이 사이트는 온라인 쇼핑몰이며,  상단에 아래 정도의 메뉴가 항상 표시된다. (난 이사이트의 물건들은 가격이 비싸서 사용하지 않는다.)
[ adafruit.com의 기본 메뉴 바 ]
상단의 메뉴를 제외 하면 아래와 같은 인터페이스 이다. 데이터 업로드 빈도를 잘 조절하면 1일치 트랜드를 볼 수도 있다. (대략 2분에 1번 정도면 하루분량이 된다.)
이미지는 아래와 같은 형태로 표시된다.



3. io.adafruit.com을 사용하다.

 사용하려면 반드시 회원가입은 해야 한다. io.adafruit.com 방문후 회원에 가입하자.  사용방법은 크게 어려운 것은 없다. 처음 사용하시는 분들을 위하여 간략하게 요약하면 다음과 같다.
 . feed : 1개의 센서값을 서버로 올린다면 1개의 feed가 생성된다. 직접 생성하지 않고 서버로 데이터를 전송하면 저절로 생성되나, 이미지 데이터를 업로드 하려면 feed를 생성하고 미리 설정을 해 주어야 한다. 최대 feed개수는  한정되어 있다.
 
 . dashboard : 사용자에게 공개되는 화면 정도로 이해하면 된다. 화면이 10개라 하면 이것을 10개 만들면 된다. 하지만 경험상 2개 이상 필요하지 않았다. 여러가지 이유가 있지만 굳이 화면을 분리할 필요성을 느끼지 못했기 때문이다. 기본적으로 로그인을 해야만 정보를 볼 수 있지만 편의성을 위하여 public으로 변경하면 로그인 없이 열람이 가능하다. 다만 public으로 설정하면 모든 사람이 열람 가능하다는 것은 염두해 두어야 한다.

* 이미지를 업로드 하기 위해서는 Feed 생성후에 아래와 같이 Feeds를 클릭하여 이미지 feed를 클릭한다.
그 후에 표시되는 화면에서 Feed History를 꼭 Off로 해야 한다. 그렇지 않은 경우 이미지를 볼 수 없다. 큰 이미지는 업로드 조차 안된다. 또한 이미지 업로드는 꼭 Base64로 인코딩을 해서 업로드 해야 한다. 또한 인코딩된 문자열의 크기가 60KB 정도 되면 adafruit에서 받아주질 않았다. 더 큰 크기의 이미지를 받아준다는 내용을 관련 포럼에서 본 것 같은데, 아직 성공하지 못했다. 나는 현재  50KB 이하로만 업로드 하고 있다.

feed 관련해서는 아래의 링크로 확인 가능하다.
https://learn.adafruit.com/adafruit-io-basics-feeds

dashboard를 꾸미는 것도 어렵지 않다. 각 콤포넌트를 손쉽게 추가, 삭제 및 배치가 가능하며 크기 조절또한 쉽다.

일반적인 온습도 정도의 정보를 사이트에 올리고 웹페이지에서 볼 수 있는 것은 adafruit에서 제공하는 예제를 사용하면 쉽다.

아래의 링크 내용을 따라서 진행하면 데이터 업로드가 가능해진다.
https://learn.adafruit.com/mqtt-adafruit-io-and-you/intro-to-adafruit-mqtt

4. ESP8266 + OV528 + io.adafruit.com

 adafruit에 온도, 습도를 업로드하고 1일 데이터 분량을 로깅하다 보니 남모를 자신감에 휩싸인 나머지 이미지를 업로드할 계획을 세웠다.  ESP8266을 처리및 통신 모듈로 하고, OV528 카메라 모듈을 연결하여 adafruit에 업로드 하기로 결심하고 작업에 착수하였다. ( 내 블로그의 다른 글로 카메라 모듈 관련해서 소개한 바 있다. )

이미지를 adafruit에 업로드하는 예제는 adafruit에서 라즈베리파이 쪽만 제공하였다. Arduino쪽은 전혀 정보가 없었기 때문에 힘든 부분이 있었다. (그래도  base64로 인코딩 해야 한다는 정도는 다행히 알고 있었다. )

 ESP8266(일반 Arduino)는 동작에 사용 가능한 메모리가 80KB 정도이다. 이것은 프로그램 저장 공간과는 별도이다. 전역변수 및 지역변수를 80KB 내에서 다 해결해야 한다. 이미지를 처리하기에는 그렇게 여유롭지 못한 공간이다. 참고로 40KB의 데이터를  base64로 인코딩하면 53KB의 크기가 된다. 아무 생각없이 코딩하면 ESP8266은 그냥 죽어 버린다.  adafruit에서 제공하는 mqtt 통신용 라이브러리가 메모리 최적화 된 그런 코드는 아니다.  소스코드를 살펴보니 150바이트 정도의 데이터만 서버로 전송할 수 있게 만든 코드였다. 메시지를 보낼 내용 전체를 만들고서 전송하는 방식으로 코딩되어 있어서 이미지를 처리할 수가 없었다. 그래서 몇개의 함수를 추가하였다. 맨 처음 헤더 부분만을 서버로 전송하는 함수, 이미지 데이터를 작은 크기로 카메라 모듈에서 가져와 base64로 인코딩한 후 서버로 전송 함수 등을 만들었다.
 글로 써보니 참 간단하다. 하지만 이를 위해서 mqtt 메시지의 형식도 공부하고,  adafruit mqtt 라이브러리도 분석했고, adafruit의 서버가 반응하는 것도 공부했다.  1개의  feed로 전송 가능한 크기가 대략 50KB 수준이라는 것도 많은 시도 끝에 알게된 것이다.

핵심 부분의 코드를 대략 적으로 기술하면 아래와 같다.

1. 내가 사용한 카메라 제어 소스코드
https://github.com/binzume/ov528cam/blob/master/arduino/HelloServer/ov528.h
참고로 자신의 카메라 모듈에 따라서 일부 코드는 수정해 주어야 한다.
내가 구매한 모듈은 camera_sync 부분을 수정해 주어야 했다. response가 약간 다르게 들어왔다.


2. 카메라에 사용 부분 (메인 코드 부분에 아래의 내용을 실행한다.)
  dCameraCheck = camera_get_data(0x00, //inputbuffer
    [](void* ctx, uint32_t sz) {
    dImageTotalSize = sz;
    Serial.println("");
    Serial.println("jpg size");
    Serial.println(dImageTotalSize);
    // mqtt 헤더 부분을 작성한다 (특히 Adafruit쪽에 맞게끔 포멧 )
    // 작성된 헤더를 서버로 보낸다
    out_cam.publish_image_start(sz);
    },
    [](void* ctx, uint8_t *buf, uint16_t len) {
    // Serial.println(len, DEC); //print the humidity
    out_cam.publish_image_block(buf, len);
    dImageDownloadSize += len;
    // 이미지를 다 다운 받은 것이면 완료된 상태이다
    if (dImageDownloadSize == dImageTotalSize)
        dStatusFlag = 2;
    yield();
    }
  );

3.Adafruit MQTT 의 라이브러리에 추가한 함수 2개

//초기 이미지 데이터의 크기만 받고 관련된 내용을 준비해서 전송한다
bool Adafruit_MQTT::publish_image_start(const char *topic, uint16_t bLen, uint8_t qos) {
    uint8_t realbuf[128];
    uint8_t *p = realbuf;
    uint8_t *start = realbuf;
    uint16_t realSize;
    uint16_t len=0;

    // calc length of non-header data
    len += 2; // two bytes to set the topic size
    len += strlen(topic); // topic length
    if(qos > 0) {
        len += 2; // qos packet id
    }
    len += (4 * (bLen / 3) + (bLen % 3 != 0 ? 4 : 0)); // payload length
    // Now you can start generating the packet!
    p[0] = MQTT_CTRL_PUBLISH << 4 | qos << 1;
    p++;

    // fill in packet[1] last
    do {
        uint8_t encodedByte = len % 128;
        len /= 128;
        // if there are more data to encode, set the top bit of this byte
        if ( len > 0 ) {
            encodedByte |= 0x80;
        }
        p[0] = encodedByte;
        p++;
    } while ( len > 0 );

    // topic comes before packet identifier
    p = stringprint(p, topic);

    // add packet identifier. used for checking PUBACK in QOS > 0
    if(qos > 0) {
        p[0] = (packet_id_counter >> 8) & 0xFF;
        p[1] = packet_id_counter & 0xFF;
        p+=2;

        // increment the packet id
        packet_id_counter++;
    }

    if (!sendPacket((uint8_t *)realbuf, p - start))
        return false;

    return true;
}

bool Adafruit_MQTT::publish_image_block(uint8_t *src, uint16_t bLen) {
    int packSize = encode_base64(src, base64_buffer, bLen);
    base64_buffer[packSize] = 0; // 문자열 마지막을 널 처리
    if (!sendPacket((unsigned char *)base64_buffer, packSize))
        return false;
    return true;
}


5. 끝으로

 문제 해결의 길은 끝이 없는 것 같다. 산너머 산이다. 테스트중인 태양광 모듈은 기대했던 것보다 정말 적게 전력이 생산되었고, 카메라 모듈은 생각보다 전력 소비량이 컸다.  카메라 모듈 설명서에 나온 제어 명령어로 Power down을 시도했지만 제대로 동작하지 않는다.  흰머리가 늘어나는 것은 현실이다. 하지만 어쩌랴, 게임을 그리 좋아하는 나도 지금 이 순간이 즐거운 것을....










댓글 없음:

댓글 쓰기

3단 6핀 스위치로 DC 모터의 회전 방향을 바꾸어 보자

1. 필요는 연구의 어머니 항상 느끼는 부분이다. 필요하지 않으면 연구하지 않으며, 필요하면 연구한다. DC 모터를 조건에 따라서 정방향 또는 역방향으로 회전시켜야 하는 필요가 생겼다. 처음에는 MCU 및 Relay Switch를 이용하는 방법을 생각...