카테고리 없음2024. 2. 26. 23:28


조금은 철 지난 이야기지만 ChatGPT로 프로그래밍이 가능한 세상입니다. 

가능하긴 한데 어느 정도 수준까지 가능한지 궁금하기도 하고 

사놓고 묵혀두고 있던 m5stack 기기가 있어 무작정 시도해 보기로 했습니다. 

 

준비물 : ChatGPT, m5stack, 개발용PC

 

이번에 사용할 core2 for AWS IoT Kit 입니다

 

m5stack은 esp32 기반의 임베디드 개발 kit? 정도 되려나요? 

 

컨트롤러 unit은 작은 용량의 배터리를 포함하고 독립적으로 프로그램을 돌리거나

다른 모듈과 결합해 원하는 기능을 구현할 수 있습니다. 

기본적으로 touch screen과 가속도 센서 등을 포함하고 있고

m5stack 스토어에서 판매하는 다른 모듈을 결합해 기능을 손쉽게 확장할 수도 있습니다.

 

Arduino나 espressif를 이용해 c++ 프로그램으로 개발도 가능하지만

기본적으로 지원하는 micropython을 이용해 간단한 개발을 해보려고 방향을 잡았습니다. 

 

업무적으로 진동을 측정할 일이 있어서 회사에 있는 진동계를 보던 중에

m5stack 기기가 생각나 이걸로 흉내 내 볼 수 있겠다는 생각이 들어 시도해 보기로 했습니다. 

 

잡스럽게 코딩을 하긴 했지만, 어느 것 하나 똑 부러지게 잘하는 게 없는 수준이라

ChatGPT를 통해 어느 수준까지 가능한지 가늠해 보기도 나쁘지 않다는 생각이 들었네요.

 

개발 환경이라고 할 것도 없이 USB-C Cable을 통해 PC에 연결하고

http://flow.m5stack.com 에 접속하면 간단히 기기의 key 세팅을 통해 개발을 시작할 수 있습니다. 

스크래치와 비슷하게 UIflow라고 하는 블록 기반의 코딩도 지원하지만 손에 잘 안 익는 것 같습니다. 

그리고 이번에는 Chat GPT에게 모두 시킬 생각이니 micropython 탭을 선택해 줍니다.

 

Blocky 또는 Python을 선택해서 개발 진행이 가능합니다.

 

프로그램이라고 부르기도 민망하지만

기본 내장된 가속도 센서를 이용해 진동을 측정하고 측정되는 진동값을 화면의 적당한 위치에 표시하고

로그를 쌓고 csv로 저장할 수 있는 프로그램을 작성하는 게 목표입니다.  

 

여력이 된다면 배터리를 사용하는 기기니까 현재 배터리 수준까지 화면에 표시해 볼 생각으로 시작했습니다. 

 

우선 다짜고짜 물어봤습니다. 

 

(참고로 실제 되는지 먼저 시도해 보고 되는 걸 확인하고 블로그 글에 올리는 거라 처음 받은 코드랑

코드 부분 부분이 다르지만 작동은 한다는 점 이해 부탁 드립니다.)

기본적인 설명 후 코드를 출력해 줍니다.

from m5stack import lcd
import imu

# 가속도 센서 초기화
imu_sensor = imu.IMU()

# 화면 초기화
lcd.clear()

while True:
    # 가속도 데이터 읽기
    ax, ay, az = imu_sensor.acceleration

    # 화면에 데이터 표시
    lcd.setCursor(0, 0)
    lcd.print("Acceleration Data:\n")
    lcd.print("X: {:.2f} m/s^2\n".format(ax))
    lcd.print("Y: {:.2f} m/s^2\n".format(ay))
    lcd.print("Z: {:.2f} m/s^2\n".format(az))

 

 

이 상태에서 돌려보고 작동되는 것을 확인한 다음 추가 기능을 구현하기 위해 

Chat GPT 프롬프트에 하나씩 요구 사항을 적기 시작했습니다. 

 

실제 사용했던 프롬프트 텍스트를 참고 삼아 아래에 적어 둡니다. 

 

1. 화면 우측 상단에 배터리 수준을 표시하는 코드를 추가해 줘

2. 화면 하단에 실시간으로 측정되고 있는 진동 수준을 그래프로 표현하는 코드를 추가해 줘

3. 측정된 진동값을 csv 파일로 로깅할 수 있는 코드를 추가해 줘

4. A 버튼을 누르면 로깅을 시작하고 다시 누르면 중지하는 코드를 추가해 줘

5. B 버튼을 누르면 그래프를 리프레쉬할 수 있는 코드를 추가해 줘

6. C 버튼을 누르면 SD 카드의 로그 목록을 보여주는 코드를 추가해 줘

 

한 번에 다 적어도 잘 알아먹을 겁니다. 

위 프롬프트를 통해 최종적으로 코드를 획득하고 

색상 등은 제가 간단히 수정해서 프로그램을 완성했습니다. 

 

사용하지 않은 코드도 있지만 그냥 남겨두었습니다. 

 

기기의 특성상 sd 카드 마운트가 잘 됐는지 확인하기 쉽지 않아 

확인하는 코드를 넣어두기도 했고

레이블을 넣었다고 의도랑 달라서 주석처리 해 버린 부분도 있습니다. 

 

어쨌든 m5stack에서 의도대로 작동되는 프로그램을 짜서 넣을 수 있었고 

혼자 짜려고 시도하는 것보다 훨씬 빠르게 프로그래밍이 가능하다는 것을 알 수 있었습니다. 

초짜 프로그래머들 설자리가 빠르게 사라질 것 같다는 생각과 함께

잘 쓰면 코딩 효율이 엄청 좋아질 것 같다는 생각도 드네요. 

 

프로그램 짜서 돌리다가 에러메시지 뜨면 굳이 메시지가 뭔지 볼 필요 없이 

ChatGPT에게 물어보면 그냥 해설을 다 해줍니다. 적절한 해결책과 함께 말이죠.

# 필요한 라이브러리를 임포트합니다.
from m5stack import *
from m5ui import *
from uiflow import *
import imu
import time
import os

setScreenColor(lcd.BLACK)
print(os.listdir('/sd'))
imu0 = imu.IMU()



# UI 컴포넌트를 초기화합니다.
label0 = M5TextBox(13, 54, "Text", lcd.FONT_Default, lcd.RED, rotate=0)
label1 = M5TextBox(13, 87, "Text", lcd.FONT_Default, lcd.GREEN, rotate=0)
label2 = M5TextBox(13, 120, "Text", lcd.FONT_Default, lcd.BLUE, rotate=0)
label3 = M5TextBox(13, 10, "Text", lcd.FONT_Default, 0xFFFFFF, rotate=0) # 시각을 표시할 레이블 추가
label4 = M5TextBox(135, 110, "", lcd.FONT_Default, 0xFF0000, rotate=0) # 로깅 상태를 표시할 레이블 추가, 위치와 폰트 크기 조정
label5 = M5TextBox(200, 10, "", lcd.FONT_Default, 0xFFFFFF, rotate=0) # 배터리 상태를 표시할 레이블 추가
label6 = M5TextBox(120, 50, "", lcd.FONT_Default, 0xFFFFFF, rotate=0) # 로그 파일 목록을 표시할 레이블 추가
label7 = M5TextBox(10, 155, "1G", lcd.FONT_Default,lcd.WHITE, rotate=0)
label8 = M5TextBox(10, 185, "0G", lcd.FONT_Default,lcd.WHITE, rotate=0)
label9 = M5TextBox(10, 215, "-1G",lcd.FONT_Default,lcd.WHITE, rotate=0)

accel_data = []
logging = False
log_visible = False # 로그 파일 목록의 상태를 저장하는 변수 추가
file_num = 0
last_save_time = 0  # 마지막 데이터 저장 시간을 추적하기 위한 변수

# 가속도 센서의 X, Y, Z 축 값을 저장할 리스트를 초기화합니다.
x_values, y_values, z_values = [], [], []

def update_graph(x_values, y_values, z_values):
    #lcd.rect(0, 120, 320, 160, lcd.WHITE) # 그래프 영역 지우기
    for i in range(1, len(x_values)):
        lcd.line(30+(i-1), 220-x_values[i-1], 30+i, 220-x_values[i], lcd.RED)
        lcd.line(30+(i-1), 220-y_values[i-1], 30+i, 220-y_values[i], lcd.GREEN)
        lcd.line(30+(i-1), 220-z_values[i-1], 30+i, 220-z_values[i], lcd.BLUE)
      #lcd.rect(0, 180, 320, 160, lcd.BLACK) # 그래프 영역 지우기
      #  if i % 20 == 0:  # 20개의 데이터마다 레이블을 그립니다.
      #      lcd.text((i-1), 200, str(x_values[i-1]), lcd.RED)
      #      lcd.text((i-1), 210, str(y_values[i-1]), lcd.GREEN)
      #      lcd.text((i-1), 220, str(z_values[i-1]), lcd.BLUE)

def save_data_automatically():
    global last_save_time
    current_time = time.ticks_ms()
    if (current_time - last_save_time) >= 3600000:  # 5분 = 300초 = 300,000 밀리초
        save_data_to_file()
        last_save_time = current_time

def buttonA_wasPressed():
  global logging, file_num
  logging = not logging
  if logging:
    label4.setText("Logging")
    # 로깅을 시작할 때 파일 번호를 증가시키고, 빈 데이터 리스트를 초기화합니다.
    file_num += 1
    global accel_data
    accel_data = []
  else:
    label4.setText("Paused")
    save_data_to_file()  # 로깅을 중지할 때 데이터를 파일에 저장합니다.

btnA.wasPressed(buttonA_wasPressed)

def refresh_graph_area():
    # 그래프 영역을 지우는 로직, 여기서는 전체 그래프 영역을 검은색으로 채웁니다.
    lcd.fillRect(30, 130, 320, 130, lcd.BLACK)

def buttonB_wasPressed():
    # 그래프 영역을 새로고침하는 함수를 호출합니다.
    refresh_graph_area()
    # 필요한 경우, 그래프 데이터 리스트를 초기화할 수도 있습니다.
    global x_values, y_values, z_values
    x_values, y_values, z_values = [], [], []

btnB.wasPressed(buttonB_wasPressed)

def buttonC_wasPressed():
  global log_visible
  save_data_to_file()
  if not log_visible:  # 로그 파일 목록이 보이지 않는 경우
    try:
      files = os.listdir('/sd')  # SD 카드의 파일 목록을 가져옵니다.
      log_files = [f for f in files if f.endswith('.csv')]  # .txt 파일만 선택합니다.
      display_text = '\n'.join(log_files) if log_files else "No CSV files found"
      #print(os.listdir('/sd'))
    except Exception as e:
      display_text = "Error accessing /sd"
    label6.setText(display_text)  # 화면에 로그 파일 목록을 표시합니다.
    log_visible = True  # 로그 파일 목록의 상태를 업데이트합니다.
  else:  # 로그 파일 목록이 보이는 경우
    label6.setText("")  # 로그 파일 목록을 지웁니다.
    log_visible = False  # 로그 파일 목록의 상태를 업데이트합니다.

btnC.wasPressed(buttonC_wasPressed)


# 데이터를 파일로 저장하는 함수
def save_data_to_file():
    global accel_data, file_num
    if accel_data:
        # SD 카드가 마운트된 기본 경로 설정
        base_path = '/sd'
        filename = '{}/accel_data_{}.csv'.format(base_path, file_num)
        
        try:
            # 지정된 경로가 존재하는지 확인하고, 없으면 생성
            #if not os.path.exists(base_path):
            #    os.makedirs(base_path)
            
            # 파일에 데이터 쓰기
            with open(filename, 'w') as f:
                for data in accel_data:
                    f.write(','.join(map(str, data)) + '\n')
            print("Data saved to:", filename)
            file_num += 1  # 다음 파일 번호로 업데이트
            accel_data = []  # 데이터 리스트 초기화
        except Exception as e:
            print("Failed to save data:", e)


while True:
    x, y, z = imu0.acceleration
    label0.setText('X: ' + str(x))
    label1.setText('Y: ' + str(y))
    label2.setText('Z: ' + str(z))
    timestamp = time.ticks_ms() # 현재 시각을 밀리초로 가져옵니다.
    t = time.localtime(timestamp // 1000 ) # 밀리초를 초로 변환하고, 시간을 서울 시간으로 변환합니다.
    current_time = "{:02d}:{:02d}:{:02d}".format(t[3], t[4], t[5]) # 시간, 분, 초를 24시간 형식으로 변환합니다.
    label3.setText('Time: ' + current_time) # 화면에 시각 표시
    batVoltage = power.getBatVoltage() # 배터리 전압을 가져옵니다.
    batPercentage = (batVoltage - 3.2) * 100 if batVoltage > 3.2 else 0 # 배터리 잔량을 계산합니다[1].
    label5.setText('Battery: {:.1f}%'.format(batPercentage)) # 화면에 배터리 상태 표시

    accel_data.append([timestamp, x, y, z])
        
    x_values.append(int((x+1)*30)) # 값의 범위를 -1~1에서 0~60으로 변환합니다.
    y_values.append(int((y+1)*30)) # 값의 범위를 -1~1에서 0~60으로 변환합니다.
    z_values.append(int((z+1)*30)) # 값의 범위를 -1~1에서 0~60으로 변환합니다.
    update_graph(x_values, y_values, z_values)

    if logging:
        save_data_automatically()

    #if len(x_values) > 30000:  # 예를 들어 배열 크기가 특정 크기를 넘어서면 자동으로 저장
    #    save_data_to_file()
    #    print(accel_data)

 

 

상용 프로그램 개발 분야에도 활용이 가능할지는 잘 모르겠으나

간단한 코딩에 활용하는 것은 충분히 가능하다는 결론이고

짠 프로그램을 다른 언어로 변환하는 것도 잘 되는 것 같았습니다.

 

마지막으로 m5stack에서 프로그램이 작동하는 화면을 올리면서 글을 마무리하려고 합니다. 

 

Posted by BlueIris


기존에 타던 차가 9년차(만 8년), 17만 6천 km가 됐습니다.

차가 오래되서라고 하기엔 요즘은 저 정도 주행거리는 아무 것도 아닐만큼 흔한 숫자죠?
아직까지 쌩쌩하기도 하고 9년된 차 치고는 나름 깨끗한 편이긴 한데...

비교적 최근에 사고가 나서 대차받은 차가 K7이었는데
그때부터 좀 더 큰 새차에 관심이 생긴거 같기도 합니다.
차가 너무 편하고 좋더라구요...ㅠ
(10년 가까이 된 차보다 최근 나온 어떤 차를 타더라도 다 좋아보였을거 같긴 합니다만...ㅋ)

그리고
올해의 어마어마한 더위에 에어컨 수리만 2회, 운전석 창문 고장으로 거의 1주일을 문을 반쯤 열고 다니며 더위에 고생을 했더니 차에 정이 뚝 떨어져 버린 것도 한몫 한 것 같네요.

차를 바꾸고 싶으니 아무 핑계나 다 가져다 붙이는 거죠ㅎ

그냥 미친척 새 차가 사고 싶어져서 한동안 특정 차를 뚫어져라 쳐다보고, 시승하고, 인터넷에서 견적 받아보기를 수차례...
결국 사고(?)를 쳤습니다.

생애 첫 차량으로 옛날 뉴코란도 suv(아버지 차)를 타면서 다음엔 절대 suv는 안 타겠다라고 생각했죠.
너무 시끄럽고... 장거리 여행때 피곤하기도 하고...
(너무 옛날 차라 그랬겠지만 ㅋ)

2번째 차로 라세티 프리미어 2.0을 선택했습니다.
현기차 말고 다른차를 사야겠다는 생각을 하니, 그닥 선택지가 없었죠...
이 차를 9년 타면서 다음엔 디젤은 절대 안 타야지 라는 생각을 하게 됐죠. 가능하면 4기통 보다 큰 엔진을 가진 차였음 좋겠다고 생각도 했네요.
연비도 나름 만족스럽고 주행감도 좋고 나름 작은 차체에 2.0 디젤이라 힘도 괜찮았습니다.
다만 국산차라고 하기엔 비싼 보험료와 수리비가 ㅠㅠ
이 부분은 나중에 기회가 되면 다른 포스트로...

그래서 3번째로 선택한 차는 3300cc 트윈터보차져의 제네시스 G70입니다.
기존보다 크면서(눈꼽만큼...;;) 가솔린 엔진, 보험료 + 수리비가 저렴할 것으로 예상되는 국산차입니다.
가족을 위한 차를 고민하다가 그랜져나 k7이 답이라고 생각했으나... 과한 욕심을 부렸네요.
실내 공간을 생각했으면 G80 제일 낮은 트림도 가능했는데, 아빠로서의 책임감은 그냥 내 던져 버렸습니다.

현재 G70의 주행거리는 111km 차 받은지 1주일.
거의 아파트 지하 주차장에 봉인 상태네요 ㅋ
아직 비닐도 다 못 뜯었습니다.
출근하기 바빠서;;;


아빠로서의 책임감을 잠시 잊어버릴만한 이쁜 차네요 ㅠㅠ

목표는 차량 탑승일기(?)을 꾸준히 적어보는 것 입니다.
기존 차와 비교도 좀 하고...

언제가 될 진 모르겠지만 철없는 아빠의 다음차 목표는 과급기 없는 자연흡기 엔진 차량입니다!
훗...

차 2대를 가진 생활이 시작됐습니다.
잘 유지할 수 있길...

Posted by BlueIris