STM32를 활용한 스마트 팜 만들기

임시온·2023년 1월 9일
0
post-thumbnail

프로젝트 개요

개발동기

코로나 및 사회적 격리 장기화 또는 코로나 사태 이후 경제적으로 어려워지거나 사람들을 만나기 힘들어지면서 우울감·외로움과 고독을 느끼는 사람들이 늘어나고 있는 추세 인터넷의 취약계층 들은 인터넷 정보의 접근성도 취약하다. 경로당에서 친구들하고 만나는 게 전부였을 만큼 스트레스를 풀 수 있는 자원 자체가 빈약하다. 불안감은 높은데 고립되는 것이 쉬움
➜ 우울감 , 외로움 등을 느끼는 사람들이 많이 생긴 것을 인지 , 이를 해결해줄 제품이 필요하다고 느낌.

프로젝트 작품 원리 설명

조명 : RGB LED를 사용하지만 브라질의 기술자인 알프레도 모제가 개발한 페트병과 물, 표백제만으로 전구의 밝기와 맞먹는 페트병 전구에 LED를 4방향으로 설치하여 작은 전력으로 더 밝은 빛을 낼수 있도록 함.
-> 환경오염으로 문제가 심각한 현대에 작은 전력으로 충분한 빛을 제공해줄수 있음.

라즈베리파이
  • 카메라 모듈과 연결하여 사용자가 web에서 접속하여 식물의 상태를 확인하고 정기적으로 기록할수 있도록 해준다.
  • stm32와 UART 통신을 사용하여 웹에서 RGB LED의 색을 바꿀수 있도록 해준다.
  • 웹을 통해서 선인장이 심어지고나서 30일 주기로 물을 줄수 있도록 알림서비스를 제공한다. -> 식물을 연구하거나 기록하는 사람일 경우에는 카메라 모듈로 기록을 해주는것이 편하게 느껴짐.
STM32F103RB 보드를 사용합니다.

1. RGB LED 제어하기

사용하는 모듈은 ws2812B RGB LED 이다.

1-1 clock 설정하기

TIM의 DMA를 사용하여 RGB LED를 사용할 예정임으로 CLOCK 을 맞춰야됨
ws2812B 모듈의 data sheet 에 따르면 1 bit에 high 와 low비가 얼마정도여야 high로 인식하는지가 나와 있다.STM32F103RB 보드의 최대 클럭은 72MHz임으로 Counter Period 값을 90을 줌으로써 1비트에 1250ns를 맞출 수 있다. (72MH 1250n = 90) 위와 같은 공식을 활용하여 HIGH 와 LOW 는 50 과 25로 구할 수 있다.
(LOW => 72MH
0.35us = 25.20 | HIGH => 72MH * 0.7us = 50.40 소수점은 버림)
이렇게 설정한 클럭은 TIM에 할당함( TIM로 DMA사용해서 제어할 꺼임 )DMA설정

코드

#include "main.h"

#include <stdlib.h>
#include <string.h>

TIM_HandleTypeDef htim3;
DMA_HandleTypeDef hdma_tim3_ch1_trig;

#define LED 12 // 사용하는 RGB LED 키는 모듈의 LED 개수를 뜻함
#define Lo 25  // LED LOW
#define Hi 50  // LED HIGH

uint8_t buf[LED*24+48]={
		Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,// Green 0
		Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,// Red 1
		Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,// Blue 2
		Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,Hi,// White 3
		Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,Hi, //4
		Hi,Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,
		Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,Hi,
		Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,Hi,
		Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,Hi,
		Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,Hi,
		Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,Hi,
		Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,Hi,
		Lo,Lo,Lo,Lo,Lo,Hi,Lo,Lo,Hi,Hi,Hi,Lo,Hi,Hi,Hi,Hi,Lo,Lo,Lo,Hi,Lo,Hi,Lo,Hi,
        };
uint8_t s_buf[LED*24+48]={
	Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
    Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,Lo,
};

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_TIM3_Init(void);
static void MX_USART3_UART_Init(void);
static void MX_USART2_UART_Init(void);

MX_GPIO_Init();
MX_DMA_Init();
MX_TIM3_Init();
MX_USART3_UART_Init();
MX_USART2_UART_Init();
  
void convert_color(uint32_t color , uint16_t led_no)
{
	for (int i=0; i<LED; i++)
	{
    	buf[led_no*LED+i] = 57;
		if ( color&(1<<((LED-1)-i)))
		{
			buf[led_no*LED+i] =Hi;
		}

		else
		{
			buf[led_no*LED+i] =Lo;
		}
	}
}

HAL_TIM_PWM_Stop_DMA(&htim3,TIM_CHANNEL_1);
HAL_TIM_PWM_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t*)buf,LED*24+48);
while(hdma_tim3_ch1_trig.Instance->CNDTR>0);
HAL_Delay(3000);
char data[10];

memset(buf,0,LED*12+48);

while (1) {
	  for (int i=0;i<4;i++)
	      for (int i=0;i<12;i++)
	  	  {
		      convert_color(rand(),i);
	      }
	      HAL_TIM_PWM_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t*)buf,LED*12+48);
	      while(hdma_tim3_ch1_trig.Instance->CNDTR>0);
	      HAL_Delay(100);
	      HAL_Delay(1000);
  	  }
 }

DMA / TIMER TRIG를 활용해서 LED 배열에 있는 걸 랜덤하게 색을 바꿔주는 코드 https://youtube.com/shorts/P4mBGVFVmnA <- LED


시연 영상 보고 싶음 보셈

2. UART 통신

UART를 활용하여 라즈베리파이로 부터 사용자에게 입력데이터를 전달받음(LED를 킬 것인지 끌 것인지) 이러한 데이터를 받으면 STM32를 활용하여 RGB LED를 제어한다.

2-2 설정

UART는 UART3과 UART2 두개를 사용한다. 이유는 한개로 하였을 때 통신이 불완전 했기 때문이다.

UART 2와 3은 설정이 같다.

코드

#include <stdlib.h>
#include <string.h>
void led_off(uint16_t led_no)
{
	for (int i=0; i<24; i++)
	{
		buf[led_no*24+i] = Lo;
	}
}
void led_on(uint16_t led_no)
{
	for (int i=0; i<24; i++)
	{
		buf[led_no*24+i] = Hi;
	}
}
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM3_Init();
  MX_USART3_UART_Init();
  MX_USART2_UART_Init()
  HAL_TIM_PWM_Stop_DMA(&htim3,TIM_CHANNEL_1);
  HAL_TIM_PWM_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t*)buf,LED*24+48);
  while(hdma_tim3_ch1_trig.Instance->CNDTR>0);
  HAL_Delay(3000);
  char data[10];
  memset(buf,0,LED*24+48);
  while (1)
  {
	  HAL_UART_Receive(&huart3, (uint8_t *)data,1,0xFFFF);
//	  HAL_UART_Transmit(&huart2, data, sizeof(data), 1000);
	  if (data[0] == '0') {
		  HAL_UART_Transmit(&huart2, "0", sizeof("0"), 1000);
		  // HAL_TIM_PWM_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t*)s_buf,LED*24+48);
		  for ( int i=0; i<12; i++ )
		  {
			  led_off(i);
		  }
	  }
	  else if (data[0] == '1') {
		  HAL_UART_Transmit(&huart2, "1", sizeof("1"), 1000);
		  // HAL_TIM_PWM_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t*)buf,LED*24+48);
		  for ( int j=0; j<12; j++ )
		  {
			  led_on(j);
		  }
	  }
	  HAL_TIM_PWM_Start_DMA(&htim3,TIM_CHANNEL_1,(uint32_t*)buf,LED*24+48);
	  while(hdma_tim3_ch1_trig.Instance->CNDTR>0);
	  HAL_Delay(500);
  }
}
  • HAL_UART_Receive : UART로 온 신호를 받음 그리고 data라는 곳에다가 저장을 함.
  • HAL_UART_Transmit : UART신호를 제대로 받았는지 확인을 위해 출력을 한다.
  • HAL_TIM_PWM_Start_DMA : CCRx 레지스터에 전송하는 역할
  • 0이라는 신호를 UART에서 받으면 buf 배열에다가 0을 넣고 1이 들어오면 buf 배열에다가 1을 넣음

라즈베리파이(백엔드)

1. 카메라 제어

camera.py
from picamera2 import Picamera2 # pip install picamera2
import subprocess
from flask import jsonify
def plant_picture():
    picam2 = Picamera2()
    camera_config = picam2.create_preview_configuration()
    picam2.configure(camera_config)
    picam2.start()
    # /home/pi/CUCU_FE/COUQUE-DASSE-FRONT/front/public/myPlnat
    # /home/pi/imgs/
    p = subprocess.check_output("cd /home/pi/CUCU_FE/COUQUE-DASSE-FRONT/front/public/myPlant/; ls -l | grep ^- | wc -l; cd ../..",shell=True, encoding='utf-8')
    p =str(p)
    p = p[:len(p)-1]
    print(p)
    # picam2.capture_file(f"../imgs/pic{p}.jpg")
    picam2.capture_file(f"/home/pi/CUCU_FE/COUQUE-DASSE-FRONT/front/public/myPlant/pic{p}.jpg")
    # pic_path = f"/home/pi/imgs/pic{p}.jpg"
    pic_path = f"./myPlant/pic{p}.jpg"
    picam2.close()
    return str(pic_path)
    # return jsonify({'path':pic_path}) # ./static/imgs/pic{p}.jpg #, p
함수를 호출을 하면 사진을 찍고 사진을 저장한 다음에 그러한 저장 경로를 반환함

uart.py

import time
import serial
ser1 = serial.Serial(
    port='/dev/ttyAMA3',
    baudrate=115200,
    parity=serial.PARITY_NONE,
    stopbits=serial.STOPBITS_ONE,
    bytesize=serial.EIGHTBITS,
    timeout=1,
    dsrdtr=False
)
def send_string(degree):
    ser1.write(degree.encode())
uart 초기 설정이랑 함수를 호출시 매개변수를 받아 UART 통신으로 stm32로 보냄
app.py
from flask import Flask , render_template , Response
from flask import request
from flask_cors import CORS # pip install flask-cors
from camera import *
from uart import *
app = Flask(__name__,static_url_path='/static')
CORS(
    app,
    resources={r"/plant/*": {"origins":"*"}},
    supports_credentials=True
)
@app.route('/plant/test',methods=['GET'])
def hello():
    return 'hello flask'
@app.route('/plant/path',methods=['GET','POST'])
def take_picture():
    result = plant_picture()
    return result
@app.route('/plant/led',methods=['GET'])
def ledc_ontroll():
    is_led = ""
    led_value = request.args.get('val','test')
    if ( str(led_value) == 'true'):
        is_led = "1"
    elif ( str(led_value) == 'false'):
        is_led = "0"
    print(is_led)
    send_string(is_led)
    return led_value
def main():
    app.debug = True
    app.run(host="IP", port="PORT NUMBER")
if __name__ == '__main__':
    main()

결과물

결과물 사진 넣기 동영상도 넣기

프론트

프로젝트 역할

백엔드 , 하드웨어 제어 , 팀장
느낀점 : 맨 처음에는 어렵다고 느끼고 막막하였지만 해결하는 과정에서 재미를 느꼈다. 또한 팀장을 맡게 되어 책임감이 생겨서 각자 할 수 있는 것들을 잘 분배하여 모두가 다같이 프로젝트에 참여하게 되어서 뿌듯했다. 팀장의 역할이 굉장히 중요하다고 느꼈고 에러를 해결해 나가는 과정에서 팀원들간의 소통 과 협업 등이 중요하다고 강하게 느꼈다 .

github

2개의 댓글

comment-user-thumbnail
2024년 12월 16일

혹시 LED 납댐 어디에다가 하셨나요??? 보드는 몇번 핀에다가 연결 하셨나요???

1개의 답글