텍스트### payload(페이로드)
사용자가 보낸 원래의 데이터
페이로드(영어: payload)는 전송되는 '순수한 데이터'를 뜻한다. 페이로드는 전송의 근본적인 목적이 되는 데이터의 일부분으로 그 데이터와 함께 전송되는 헤더, 메타데이터와 같은 부분은 제외
//-------- UDP start ------------
extern struct udp_pcb *upcb1; // Add UDP Control Block
extern ip_addr_t addr1; // Add
extern uint8_t udp_data[100]; // Add
extern uint8_t udp_flag; // Add
extern uint8_t led_flag[4]; //Add
struct pbuf *p1; // Add Send buffer
char temp_str[40] = "";
uint8_t servo[5];
uint8_t led[4];
//--------- UDP end ------------
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART3_UART_Init();
MX_USB_OTG_FS_PCD_Init();
MX_USART6_UART_Init();
MX_TIM10_Init();
MX_TIM11_Init();
MX_TIM3_Init();
MX_TIM4_Init();
MX_TIM2_Init();
MX_RTC_Init();
MX_TIM5_Init();
MX_I2C1_Init();
MX_ADC1_Init();
MX_LWIP_Init();
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart3, &rx_data, 1); // assing to RX INT
HAL_UART_Receive_IT(&huart6, &bt_rx_data, 1); // for BT assing to RX INT
HAL_TIM_Base_Start_IT(&htim10); // ADD_SIKWON_1011
HAL_TIM_Base_Start_IT(&htim11); // ADD_SIKWON_1011
// HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // for count pulse(rising edge & falling edge)
// HAL_TIM_PWM_Start_IT(&htim4, TIM_CHANNEL_1); // for DC motor PWM control
// HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // for SERVO motor PWM control
// HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_4); // PIEZO Buzzer
HAL_ADC_Start_IT(&hadc1);
// DHT11_Init();
// i2c_lcd_init();
TIM10_10ms_counter=0;
// stepmotor_main_test();
// dotmatrix_main();
// dotmatrix_main_test();
// led_main();
// DHT11_main();
// i2c_lcd_main();
// servo_motor_test_main();
// buzzer_main();
//------ TCP/IP start -------------
udp_echoserver_init(); // Add TCP
upcb1 = udp_new(); //Add
IP4_ADDR(&addr1,10,10,15,70); //Add
udp_bind(upcb1,IP_ADDR_ANY,9999); //Add
//------- TCP/IP end -----------------
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/*
* 덩어리 1
*/
ethernetif_input(&gnetif);
sys_check_timeouts();
if (udp_flag == 1) // Add
{
udp_flag = 0;
strncpy(servo, udp_data + 6, 4);
HAL_UART_Transmit(&huart3, servo, strlen(servo), 1000);
//__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_4, atoi(servo));
}
if(led_flag == 1){
led_flag = 0;
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); // LED1
HAL_UART_Transmit(&huart3, led, strlen(led), 1000);
}
/*
* 덩어리 2
*/
udp_connect(upcb1, &addr1, 9999); // Add
sprintf(temp_str, "stm32->server send");
p1 = pbuf_alloc(PBUF_TRANSPORT, strlen((char *) temp_str), PBUF_POOL);
if (p1 != NULL)
{
// copy data to pbuf
pbuf_take(p1, (char *)temp_str, strlen((char *)temp_str));
// send udp data
udp_send(upcb1, p1);
/* free the UDP connection, so we can accept new clients */
udp_disconnect(upcb1);
// free pbuf
pbuf_free(p1);
}
else
{
HAL_UART_Transmit(&huart3, "pbuf not allocated\n", strlen("pbuf not allocated\n"), 10);
}
HAL_Delay(50);
#if 0
printf("cds sensor: %d\n", adcValue[0]);
if (adcValue[0] < 2500)
{
nucleo_stm32f429zi_led_on();
}
else
{
nucleo_stm32f429zi_led_off();
}
HAL_Delay(10);
#endif
// DHT11_processing();
// pc_command_processing();
// bt_command_processing();
// ultrasonic_processing();
// dcmotor_pwm_control();
// get_rtc();
// lcd_display_mode_select();
// set_time_button_ui();
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
main.c에서 ethernetif_input(&gnetif);를 만나면 콜백으로 들어감
void udp_echoserver_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
/* Connect to the remote client */
udp_connect(upcb, addr, UDP_CLIENT_PORT);
MEMCPY(udp_data, p->payload, sizeof(udp_data)); // UDP data를 1 byte 읽어온다
if (strncmp(udp_data, "SERVO:", 6) == 0)
{
/* Tell the client that we have accepted it */
udp_flag = 1;
}
if (strncmp(udp_data, "LED001", 6) == 0){
led_flag = 1;
}
/* Tell the client that we have accepted it */
udp_send(upcb, p);
/* free the UDP connection, so we can accept new clients */
udp_disconnect(upcb);
/* Free the p buffer */
pbuf_free(p);
}
콜백 인터럽트 함수는 길게 짜면 안된다. ISR은 짧게 상태 변화만 indicate하고, 그로 인해 발생해야 하는 작업은 main.c에서 돌고 있는 loop monitor에서 처리되어야 한다.
Qt - File - Qt Widgets Application으로 만들었다.
make는 컴파일을 할 때 특정 파일만 골라서 컴파일 함으로써, 디버깅 할 때마다 모든 파일을 전부 다 컴파일 하는 쓸데없는 오버헤드를 방지해주는 것이다.
우리 카페에 있음
#-------------------------------------------------
#
# Project created by QtCreator 2024-01-29T13:46:57
#
#-------------------------------------------------
QT += core gui
QT += network #add kenGwon TCP/IP
QT += printsupport #add kenGwon plot chart
...
CONFIG += c++11
SOURCES += \
main.cpp \
mainwindow.cpp \
qcustomplot.cpp
HEADERS += \
../../../Downloads/qcustomplot.h \
mainwindow.h \
qcustomplot.h
FORMS += \
mainwindow.ui
...
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QPen>
#include <QUdpSocket> // add kenGwon
#include <QTextStream> // 상위 레이어에서 메세지를 텍스트 단위로 핸들링하기 위한 것 :: add kenGwon
#include <QDebug> // stdio.h와 비슷 :: add kenGwon
#include <QString> // c++의 string을 override한 클래스 :: add kenGwon
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void add_point(double time_x, double tmp_y, double wet_y); // plot그리기용 함수 :: add kenGwon
void clear_data(); // plot 지우기 :: add kenGwon
void plot(); // plot 출력 :: add kenGwon
QVector <double> qv_tmp_x, qv_tmp_y, qv_wet_x, qv_wet_y; // plot그리기용 변수
QString temperature, wet; // plot그리기용 변수
public slots: // signal에 대한 callback function
void readyRead();
private slots:
void on_pushButtonSend_clicked();
void on_checkBox_LED1_stateChanged(int arg1);
void on_checkBox_LED2_stateChanged(int arg1);
void on_checkBox_LED3_stateChanged(int arg1);
void on_dial_servo_valueChanged(int value);
void on_dial_led_valueChanged(int value);
void on_horizontalSlider_DHT11_interval_valueChanged(int value);
void on_pushButton_clear_clicked();
private:
Ui::MainWindow *ui;
QUdpSocket *socket = nullptr;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "qcustomplot.h" // add kenGwon
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* kenGwon START */
// 1. 소켓 생성
socket = new QUdpSocket(this);
// 2. IP, SW PORT번호 등록
bool result = socket->bind(QHostAddress::AnyIPv4, 9999);
qDebug() << result; // c++의 cout과 동일한 역할을 하는게 qDebug()이다.
if (result) {qDebug() << "pass!!!";}
else {qDebug() << "fail...";}
// 3. Qt signal(event발생) / slot(event에 대한 interrupt service routine) / connect(signal과 slot을 mapping)
connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead())); // 소켓으로부터 읽을 데이터가 존재하면 this의 readyRead()함수를 호출한다는 것
ui->plot->setInteraction(QCP::iRangeDrag, true); // 여기서 plot이라는 멤버는 .ui GUI에서 설정한 Object Name이다.
ui->plot->setInteraction(QCP::iRangeZoom, true);
ui->plot->addGraph();
ui->plot->addGraph();
//ui->plot->xAxis->setLabel("time(s)");
ui->plot->yAxis->setLabel("temp");
ui->plot->yAxis->setRange(0.0, 40.0);
QPen red_pen, blue_pen;
red_pen.setColor(QColor(255, 0, 0));
blue_pen.setColor(QColor(0, 0, 255));
ui->plot->graph(0)->setScatterStyle(QCPScatterStyle::ssCircle); //점찍는 스타일 결정.
ui->plot->graph(0)->setLineStyle(QCPGraph::lsLine); //라인 스타일 결정.
ui->plot->graph(0)->setPen(red_pen);
ui->plot->graph(1)->setScatterStyle(QCPScatterStyle::ssSquare); //점찍는 스타일 결정.
ui->plot->graph(1)->setLineStyle(QCPGraph::lsLine); //라인 스타일 결정.
ui->plot->graph(1)->setPen(blue_pen);
connect(ui->plot, SIGNAL(mouseDoubleClickEvent(QMouseEvent*)), SLOT(QMouseEvent*));
/* kenGwon END */
}
MainWindow::~MainWindow()
{
delete ui;
}
int time_gv = 0;
void MainWindow::readyRead()
{
QByteArray buffer;
double temp_digit; // 온도값 저장용 변수
// static int time = 0;
QHostAddress sender;
quint16 senderPort; // 송신자의 port주소
buffer.resize(socket->pendingDatagramSize());
// 소켓으로부터 데이터를 읽음
socket->readDatagram(buffer.data(), buffer.size(), &sender, &senderPort);
buffer.chop(1); // STM32로부터 수신한 버퍼에서 문자열 맨 끝에 있는 "\n"를 제거하기 위한 함수이다. (chop: 맨 오른쪽에서 1바이트 제거하는 함수)
// 읽어온 데이터를 ui의 textEdit요소에 출력(ui는 생성자에서 만들어진 멤버변수)
ui->textEditRxData->append(buffer);
ui->textEditRxData->show();
// 읽어온 데이터를 ui의 LCD number요소에 출력
// buffer = buffer.right(2); // 버퍼의 맨 오른쪽부터 두자리만 buffer의 값으로 취하겠다는 말... [Tmperature]:25
// // 버퍼에는 25가 들어있다. 25는 utf-8모드로 저장되어있다. 그래서 그걸 ascii로 바꾸고, 다시 integer로 바꿔줘야 한다.
// // LCD number요소에는 int 혹은 double 값을 줘야 올바르게 출력된다.
// temp_digit = buffer.toDouble();
// ui->lcdNumber_tmp->display(temp_digit);
// ui->lcdNumber_tmp->show();
QList<QByteArray> parts = buffer.split(' ');
// 각 부분을 확인하면서 "Tmp:"이 포함된 부분 찾기
QByteArray tmp;
QByteArray wet;
for (const QByteArray& part : parts) {
if (part.startsWith("Tmp:")) {
// "Tmp:" 다음의 부분을 추출
tmp = part.mid(4); // "Tmp:" 이후의 문자열만 추출
}
else if (part.startsWith("Wet:")) {
// "Wet:" 다음의 부분을 추출
wet = part.mid(4); // "Wet:" 이후의 문자열만 추출
}
}
if (!(tmp.toInt() == 0 && wet.toDouble() == 0))
{
ui->lcdNumber_tmp->display(tmp.toDouble());
ui->lcdNumber_tmp->show();
ui->lcdNumber_wet->display(wet.toDouble());
ui->lcdNumber_wet->show();
// 그림 그리는 부분
add_point(time_gv, tmp.toDouble(), wet.toDouble());
time_gv+=3;
ui->plot->xAxis->setRange(0, time_gv+3);
plot();
}
}
/*
* 생성경로: .ui Design GUI에서 "send버튼" 우클릭 - "go to slot" 클릭
*/
void MainWindow::on_pushButtonSend_clicked()
{
QByteArray Data;
Data = ui->lineEditSendData->text().toUtf8();
socket->writeDatagram(Data, QHostAddress("10.10.15.82"), 9999);
}
void MainWindow::on_checkBox_LED1_stateChanged(int arg1)
{
QString buffer;
QByteArray send_data;
buffer.sprintf("LED001");
send_data = buffer.toUtf8(); // ascii는 utf-8과 값이 동일하여 변환을 굳이 안해도 된다. 하지만 한글이라면 반드시 변환해줘야 한다.
socket->writeDatagram(send_data, QHostAddress("10.10.15.82"), 9999);
}
void MainWindow::on_checkBox_LED2_stateChanged(int arg1)
{
socket->writeDatagram("LED002", QHostAddress("10.10.15.82"), 9999);
}
void MainWindow::on_checkBox_LED3_stateChanged(int arg1)
{
socket->writeDatagram("LED003", QHostAddress("10.10.15.82"), 9999);
}
void MainWindow::add_point(double time_x, double tmp_y, double wet_y)
{
qv_tmp_x.append(time_x);
qv_wet_x.append(time_x);
qv_tmp_y.append(tmp_y);
qv_wet_y.append(wet_y);
}
void MainWindow::clear_data()
{
qv_tmp_x.clear();
qv_wet_x.clear();
qv_tmp_y.clear();
qv_wet_y.clear();
}
void MainWindow::plot()
{
ui->plot->graph(0)->setData(qv_tmp_x, qv_tmp_y);
ui->plot->graph(1)->setData(qv_wet_x, qv_wet_y);
ui->plot->replot();
ui->plot->update();
}
// SERVO: ~99
void MainWindow::on_dial_servo_valueChanged(int value)
{
QByteArray servo_data = "SERVO:";
servo_data.append(QString::number(ui->dial_servo->value()));
ui->lcdNumber_servo->display(ui->dial_servo->value());
socket->writeDatagram(servo_data, QHostAddress("10.10.15.82"), 9999);
qDebug() << "servo data: " << servo_data << endl;
}
void MainWindow::on_dial_led_valueChanged(int value)
{
QByteArray LED_data = "LED:";
LED_data.append(QString::number(ui->dial_led->value()));
ui->lcdNumber_led->display(ui->dial_led->value());
socket->writeDatagram(LED_data, QHostAddress("10.10.15.82"), 9999);
qDebug() << "led data: " << LED_data << endl;
}
void MainWindow::on_horizontalSlider_DHT11_interval_valueChanged(int value)
{
QByteArray DHT11_interval_data = "DHT11_interval:";
DHT11_interval_data.append(QString::number(ui->horizontalSlider_DHT11_interval->value()));
ui->lcdNumber_DHT11_interval->display(ui->horizontalSlider_DHT11_interval->value());
socket->writeDatagram(DHT11_interval_data, QHostAddress("10.10.15.82"), 9999);
qDebug() << "DHT11 interval data: " << DHT11_interval_data << endl;
}
void MainWindow::on_pushButton_clear_clicked()
{
clear_data();
time_gv = 0;
}