Giao thức HTTP (Tiếng Anh: HyperText Transfer Protocol – Giao thức truyền tải siêu văn bản) là một giao thức thuộc tầng ứng dụng ( lớp trên của giao thức TCP) nói đơn giản là nó hoạt động trên nền giao thức TCP – nó chính là trường data của giao thức TCP
Mình đã có viết một bài về giao thức http, các bạn tham khảo tại đây: http://iot47.com/iot-bai-4-gioi-thieu-ngon-ngu-html-va-mo-hinh-http-resquest-reponse/
HTTP Request
Client gửi 1 bản tin tới Server, Server tiếp nhận, phân tích và trả về nội dung mà client muốn. 1 HTTP Request cơ bản gồm các request line và data. Request line có cấu trúc như sau:
Các request line sẽ được kết thúc bằng 2 lần xuống dòng (\r\n\r\n)
Trăm nghe không bằng 1 thấy, tại sao không test thử luôn nhỉ 🙂
Sử dụng project đã làm ở bài 13, chạy mô phỏng, mở trình duyệt, gõ địa chỉ IP của ENC28j60 ( như của mình là 192.168.1.197) các bạn sẽ thấy máy tính gửi lên 1 cái truy vấn tương tự như trên
Chúng ta sẽ nghiên cứu kĩ hơn về http request ở bài TCP Client, còn bài này đang làm TCP Server nên tập trung vào http reponse nhé
HTTP Reponse
Khi nhận 1 request và phân tích request, server đã biết client muốn gì, nó sẽ trả lời lại nội dung tương ứng cho server. 1 HTTP Reponse có cấu trúc cơ bản như sau:
Về cơ bản, chúng ta có thể rút gọn các Status Line xuống còn 2 dòng như sau:
HTTP/1.1 200 OK
Content-Type: text/html
HTTP/1.1 ám chỉ phiên bản HTTP, nó đã được phát triển lên 2.0. Nhưng chưa được đưa vào sử dụng
200 là Status code, ám chỉ server đã tiếp nhận và xử lí request của client thành công
Content-Type: text/html ám chỉ phần data mà server sẽ gửi thuộc loại mã html
Các status line sẽ được kết thúc bằng 2 lần xuống dòng (\r\n\r\n)
Và sau khi gửi xong các statusline thì chính là data của server gửi xuống dưới dạng các mã HTML
Ngôn ngữ HTML
Sau khi nhận được dữ liệu Server trả về (tức các đoạn code html), trình duyệt web sẽ phân tích các mã html đó và tạo ra giao diện hiển thị cho người dùng.
Ngôn ngữ HTML được tạo bởi các cặp thẻ, nó tạo ra khung sườn cho trang Web, kết hợp với CSS và JavaScript tạo nên bộ 3 tam kiếm hợp bích.
Cấu trúc cơ bản của 1 trang HTML
1 2 3 4 5 6 7 |
<!DOCTYPE html> <html lang="en"> <head></head> <body> Đây là 1 trang web </body> </html> |
1 số thẻ thường dùng
Thẻ <h1></h1> tạo ra phần chữ tiêu đề ( chữ khá to và đậm)
Ví dụ
1 |
<h1>Đây là thẻ H1</h1> |
Đây là thẻ H1
Các bạn tự tìm hiểu thêm về ngôn ngữ html trên mạng nhé, chúng ta sẽ quay lại chủ đề chính ngày hôm nay
Tạo webserver với ECN28J60
Tiếp tục với code đã viết ở bài 13, tại chỗ
1 |
TCP_send(TCP_Struct_Frame,len,"Xin chao TCP Client\r\n"); |
Các bạn xóa dòng phản hồi này đi vì nó không cần thiết nữa, thay vào đó, mình sẽ thêm 1 lệnh kiểm tra data gửi đến xem lệnh gửi đến có phải là truy vấn GET không, nếu đúng thì trả về http reponse cùng với 1 mã html đơn giản
1 2 3 4 |
if (strncmp((char*)TCP_Struct_Frame->data,"GET /", 5) == 0) { TCP_send(TCP_Struct_Frame,len,"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n <h1>IoT47.com xin chao cac ban</h1>"); } |
Các bạn chạy mô phỏng và quay lại trình duyệt web để truy cập vào IP của enc28j60
Tuyệt ! 1 trang web siêu đơn giản đã được tạo ra !
Tạo thư viện webserver
Đó chỉ là 1 demo nhỏ thôi, để tạo 1 webserver hoàn chỉnh sẽ phức tạp hơn, vì 1 trang web lớn thì không thể gửi 1 phát xong ngay được mà phải chia nhỏ thành các gói tin để gửi lần lượt. Chúng ta sẽ làm điều đó trong 1 thư viện riêng nhé
Các bạn tạo 2 file thư viện http.c và http.h và add vào project
Thêm mã khởi tạo cho file http.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#ifndef HTTP_H_ #define HTTP_H_ //-------------------------------------------------- //Include cac thu vien can thiet #include <mega328p.h> #include <delay.h> #include <string.h> #include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <spi.h> #include "uart.h" #include "tcp.h" //-------------------------------------------------- //-------------------------------------------------- #endif /* HTTP_H_ */ |
Và mã khởi tạo cho file http.c
1 2 3 4 5 6 7 8 9 10 |
#include "http.h" extern const uint8_t macaddr[6]; extern const uint8_t ip[4]; extern uint8_t debug_string[60]; //-------------------------------------------------- |
Tóm tắt quy trình khi bạn truy cập vào webserver
- Client (tức trình duyệt) gửi bản tin connect (SYN) server trả lời và hoàn tất việc bắt tay
- Client gửi http request tới server (PSH|ACK)
- Server nhận được http reuqest và gửi trả mã html, do chip AVR có giới hạn RAM nên mình đã khởi tạo bộ đệm là 512 byte (trong net.c) , do vậy 1 lần gửi dữ liệu đi chỉ được tối đa 12 byte, lúc này sẽ có 3 trường hợp
1. Gửi gói 1 lần xong luôn
2. Gửi gói lần cuối
3. Gửi gói n lần nữa mới đến lần cuối - Sau khi gửi xong chúng ta sẽ đóng kết nối với client
Chúng ta sẽ xử lí tất cả các trường hợp nhé, trước tiên mình phải tạo ra 1 giao diện web đã, mình sẽ sử dụng giao điện web đã viết ở tutorial IOT-ESP8266 bài 5 – nó có dạng hiển thị như này
Còn đây là mã nguồn của nó
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Điều khiển thiết bị</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> .b{width: 100px;height: 40px;font-size: 21px;color: #FFF;background-color:#4caf50;border-radius: 10px;} .t{width: 100px;height: 40px;font-size: 21px;color: #FFF;background-color:#f44336;border-radius: 10px;} </style> </head> <body> <div style="width: 330px;height: auto;margin: 0 auto;margin-top: 70px"> <h1 align="center">Điều khiển thiết bị qua WIFI</h1> <table align="center"> <tr> <td><a href='/bat1'><button class='b'>Bật 1</button></a><td> <td><a href='/tat1'><button class='t'>Tắt 1</button></a><td> <tr> <tr> <td><a href='/bat2'><button class='b'>Bật 2</button></a><td> <td><a href='/tat2'><button class='t'>Tắt 2</button></a><td> <tr> <tr> <td><a href='/bat3'><button class='b'>Bật 3</button></a><td> <td><a href='/tat3'><button class='t'>Tắt 3</button></a><td> <tr> <tr> <td><a href='/bat4'><button class='b'>Bật 4</button></a><td> <td><a href='/tat4'><button class='t'>Tắt 4</button></a><td> <tr> </table> </div> </body> </html> |
Trong mã trên mình có xài kí tự tiếng việt UTF-8, mà phần mềm AVRCodevision không hỗ trợ editer utf-8 nên mình sẽ chuyển sang dạng HEX để code webserver của chúng ta hiện được tiếng Việt nhé !
Mình xài tool online này để chuyển : https://onlineutf8tools.com/convert-utf8-to-hexadecimal
Trước khi chuyển đổi thì mình cũng nhét thêm mấy dòng Status Line ở đầu luôn, như vậy toàn bộ code web sẽ như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
HTTP/1.1 200 OK Content-Type: text/html <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Điều khiển thiết bị</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> .b{width: 100px;height: 40px;font-size: 21px;color: #FFF;background-color:#4caf50;border-radius: 10px;} .t{width: 100px;height: 40px;font-size: 21px;color: #FFF;background-color:#f44336;border-radius: 10px;} </style> </head> <body> <div style="width: 330px;height: auto;margin: 0 auto;margin-top: 70px"> <h1 align="center">Điều khiển thiết bị qua WIFI</h1> <table align="center"> <tr> <td><a href='/bat1'><button class='b'>Bật 1</button></a><td> <td><a href='/tat1'><button class='t'>Tắt 1</button></a><td> <tr> <tr> <td><a href='/bat2'><button class='b'>Bật 2</button></a><td> <td><a href='/tat2'><button class='t'>Tắt 2</button></a><td> <tr> <tr> <td><a href='/bat3'><button class='b'>Bật 3</button></a><td> <td><a href='/tat3'><button class='t'>Tắt 3</button></a><td> <tr> <tr> <td><a href='/bat4'><button class='b'>Bật 4</button></a><td> <td><a href='/tat4'><button class='t'>Tắt 4</button></a><td> <tr> </table> </div> </body> </html> |
Như vậy là có cục data webserver,các bạn bỏ vào file http.c và đừng quên đưa nó vào bộ nhớ flash
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
flash uint8_t web_page[]= { 0x48 ,0x54 ,0x54 ,0x50 ,0x2f ,0x31 ,0x2e ,0x31 ,0x20 ,0x32 ,0x30 ,0x30 ,0x20 ,0x4f ,0x4b ,0x0a ,0x43 ,0x6f ,0x6e ,0x74 ,0x65 ,0x6e ,0x74 ,0x2d ,0x54 ,0x79 ,0x70 ,0x65 ,0x3a ,0x20 ,0x74 ,0x65 ,0x78 ,0x74 ,0x2f ,0x68 ,0x74 ,0x6d ,0x6c ,0x0a ,0x0a ,0x3c ,0x21 ,0x44 ,0x4f ,0x43 ,0x54 ,0x59 ,0x50 ,0x45 ,0x20 ,0x68 ,0x74 ,0x6d ,0x6c ,0x3e ,0x0a ,0x3c ,0x68 ,0x74 ,0x6d ,0x6c ,0x3e ,0x0a ,0x3c ,0x68 ,0x65 ,0x61 ,0x64 ,0x3e ,0x0a ,0x09 ,0x20 ,0x3c ,0x6d ,0x65 ,0x74 ,0x61 ,0x20 ,0x68 ,0x74 ,0x74 ,0x70 ,0x2d ,0x65 ,0x71 ,0x75 ,0x69 ,0x76 ,0x3d ,0x22 ,0x43 ,0x6f ,0x6e ,0x74 ,0x65 ,0x6e ,0x74 ,0x2d ,0x54 ,0x79 ,0x70 ,0x65 ,0x22 ,0x20 ,0x63 ,0x6f ,0x6e ,0x74 ,0x65 ,0x6e ,0x74 ,0x3d ,0x22 ,0x74 ,0x65 ,0x78 ,0x74 ,0x2f ,0x68 ,0x74 ,0x6d ,0x6c ,0x3b ,0x20 ,0x63 ,0x68 ,0x61 ,0x72 ,0x73 ,0x65 ,0x74 ,0x3d ,0x75 ,0x74 ,0x66 ,0x2d ,0x38 ,0x22 ,0x3e ,0x0a ,0x09 ,0x3c ,0x74 ,0x69 ,0x74 ,0x6c ,0x65 ,0x3e ,0xc4 ,0x90 ,0x69 ,0xe1 ,0xbb ,0x81 ,0x75 ,0x20 ,0x6b ,0x68 ,0x69 ,0xe1 ,0xbb ,0x83 ,0x6e ,0x20 ,0x74 ,0x68 ,0x69 ,0xe1 ,0xba ,0xbf ,0x74 ,0x20 ,0x62 ,0xe1 ,0xbb ,0x8b ,0x3c ,0x2f ,0x74 ,0x69 ,0x74 ,0x6c ,0x65 ,0x3e ,0x0a ,0x09 ,0x3c ,0x6d ,0x65 ,0x74 ,0x61 ,0x20 ,0x6e ,0x61 ,0x6d ,0x65 ,0x3d ,0x22 ,0x76 ,0x69 ,0x65 ,0x77 ,0x70 ,0x6f ,0x72 ,0x74 ,0x22 ,0x20 ,0x63 ,0x6f ,0x6e ,0x74 ,0x65 ,0x6e ,0x74 ,0x3d ,0x22 ,0x77 ,0x69 ,0x64 ,0x74 ,0x68 ,0x3d ,0x64 ,0x65 ,0x76 ,0x69 ,0x63 ,0x65 ,0x2d ,0x77 ,0x69 ,0x64 ,0x74 ,0x68 ,0x2c ,0x20 ,0x69 ,0x6e ,0x69 ,0x74 ,0x69 ,0x61 ,0x6c ,0x2d ,0x73 ,0x63 ,0x61 ,0x6c ,0x65 ,0x3d ,0x31 ,0x22 ,0x3e ,0x0a ,0x09 ,0x3c ,0x73 ,0x74 ,0x79 ,0x6c ,0x65 ,0x3e ,0x0a ,0x09 ,0x09 ,0x2e ,0x62 ,0x7b ,0x77 ,0x69 ,0x64 ,0x74 ,0x68 ,0x3a ,0x20 ,0x31 ,0x30 ,0x30 ,0x70 ,0x78 ,0x3b ,0x68 ,0x65 ,0x69 ,0x67 ,0x68 ,0x74 ,0x3a ,0x20 ,0x34 ,0x30 ,0x70 ,0x78 ,0x3b ,0x66 ,0x6f ,0x6e ,0x74 ,0x2d ,0x73 ,0x69 ,0x7a ,0x65 ,0x3a ,0x20 ,0x32 ,0x31 ,0x70 ,0x78 ,0x3b ,0x63 ,0x6f ,0x6c ,0x6f ,0x72 ,0x3a ,0x20 ,0x23 ,0x46 ,0x46 ,0x46 ,0x3b ,0x62 ,0x61 ,0x63 ,0x6b ,0x67 ,0x72 ,0x6f ,0x75 ,0x6e ,0x64 ,0x2d ,0x63 ,0x6f ,0x6c ,0x6f ,0x72 ,0x3a ,0x23 ,0x34 ,0x63 ,0x61 ,0x66 ,0x35 ,0x30 ,0x3b ,0x62 ,0x6f ,0x72 ,0x64 ,0x65 ,0x72 ,0x2d ,0x72 ,0x61 ,0x64 ,0x69 ,0x75 ,0x73 ,0x3a ,0x20 ,0x31 ,0x30 ,0x70 ,0x78 ,0x3b ,0x7d ,0x0a ,0x09 ,0x09 ,0x2e ,0x74 ,0x7b ,0x77 ,0x69 ,0x64 ,0x74 ,0x68 ,0x3a ,0x20 ,0x31 ,0x30 ,0x30 ,0x70 ,0x78 ,0x3b ,0x68 ,0x65 ,0x69 ,0x67 ,0x68 ,0x74 ,0x3a ,0x20 ,0x34 ,0x30 ,0x70 ,0x78 ,0x3b ,0x66 ,0x6f ,0x6e ,0x74 ,0x2d ,0x73 ,0x69 ,0x7a ,0x65 ,0x3a ,0x20 ,0x32 ,0x31 ,0x70 ,0x78 ,0x3b ,0x63 ,0x6f ,0x6c ,0x6f ,0x72 ,0x3a ,0x20 ,0x23 ,0x46 ,0x46 ,0x46 ,0x3b ,0x62 ,0x61 ,0x63 ,0x6b ,0x67 ,0x72 ,0x6f ,0x75 ,0x6e ,0x64 ,0x2d ,0x63 ,0x6f ,0x6c ,0x6f ,0x72 ,0x3a ,0x23 ,0x66 ,0x34 ,0x34 ,0x33 ,0x33 ,0x36 ,0x3b ,0x62 ,0x6f ,0x72 ,0x64 ,0x65 ,0x72 ,0x2d ,0x72 ,0x61 ,0x64 ,0x69 ,0x75 ,0x73 ,0x3a ,0x20 ,0x31 ,0x30 ,0x70 ,0x78 ,0x3b ,0x7d ,0x0a ,0x09 ,0x3c ,0x2f ,0x73 ,0x74 ,0x79 ,0x6c ,0x65 ,0x3e ,0x0a ,0x3c ,0x2f ,0x68 ,0x65 ,0x61 ,0x64 ,0x3e ,0x0a ,0x3c ,0x62 ,0x6f ,0x64 ,0x79 ,0x3e ,0x0a ,0x3c ,0x64 ,0x69 ,0x76 ,0x20 ,0x73 ,0x74 ,0x79 ,0x6c ,0x65 ,0x3d ,0x22 ,0x77 ,0x69 ,0x64 ,0x74 ,0x68 ,0x3a ,0x20 ,0x33 ,0x33 ,0x30 ,0x70 ,0x78 ,0x3b ,0x68 ,0x65 ,0x69 ,0x67 ,0x68 ,0x74 ,0x3a ,0x20 ,0x61 ,0x75 ,0x74 ,0x6f ,0x3b ,0x6d ,0x61 ,0x72 ,0x67 ,0x69 ,0x6e ,0x3a ,0x20 ,0x30 ,0x20 ,0x61 ,0x75 ,0x74 ,0x6f ,0x3b ,0x6d ,0x61 ,0x72 ,0x67 ,0x69 ,0x6e ,0x2d ,0x74 ,0x6f ,0x70 ,0x3a ,0x20 ,0x37 ,0x30 ,0x70 ,0x78 ,0x22 ,0x3e ,0x0a ,0x3c ,0x68 ,0x31 ,0x20 ,0x61 ,0x6c ,0x69 ,0x67 ,0x6e ,0x3d ,0x22 ,0x63 ,0x65 ,0x6e ,0x74 ,0x65 ,0x72 ,0x22 ,0x3e ,0xc4 ,0x90 ,0x69 ,0xe1 ,0xbb ,0x81 ,0x75 ,0x20 ,0x6b ,0x68 ,0x69 ,0xe1 ,0xbb ,0x83 ,0x6e ,0x20 ,0x74 ,0x68 ,0x69 ,0xe1 ,0xba ,0xbf ,0x74 ,0x20 ,0x62 ,0xe1 ,0xbb ,0x8b ,0x20 ,0x71 ,0x75 ,0x61 ,0x20 ,0x57 ,0x49 ,0x46 ,0x49 ,0x3c ,0x2f ,0x68 ,0x31 ,0x3e ,0x0a ,0x09 ,0x3c ,0x74 ,0x61 ,0x62 ,0x6c ,0x65 ,0x20 ,0x61 ,0x6c ,0x69 ,0x67 ,0x6e ,0x3d ,0x22 ,0x63 ,0x65 ,0x6e ,0x74 ,0x65 ,0x72 ,0x22 ,0x3e ,0x0a ,0x09 ,0x09 ,0x3c ,0x74 ,0x72 ,0x3e ,0x0a ,0x09 ,0x09 ,0x09 ,0x3c ,0x74 ,0x64 ,0x3e ,0x3c ,0x61 ,0x20 ,0x68 ,0x72 ,0x65 ,0x66 ,0x3d ,0x27 ,0x2f ,0x62 ,0x61 ,0x74 ,0x31 ,0x27 ,0x3e ,0x3c ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x20 ,0x63 ,0x6c ,0x61 ,0x73 ,0x73 ,0x3d ,0x27 ,0x62 ,0x27 ,0x3e ,0x42 ,0xe1 ,0xba ,0xad ,0x74 ,0x20 ,0x31 ,0x3c ,0x2f ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x3e ,0x3c ,0x2f ,0x61 ,0x3e ,0x3c ,0x74 ,0x64 ,0x3e ,0x0a ,0x09 ,0x09 ,0x09 ,0x3c ,0x74 ,0x64 ,0x3e ,0x3c ,0x61 ,0x20 ,0x68 ,0x72 ,0x65 ,0x66 ,0x3d ,0x27 ,0x2f ,0x74 ,0x61 ,0x74 ,0x31 ,0x27 ,0x3e ,0x3c ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x20 ,0x63 ,0x6c ,0x61 ,0x73 ,0x73 ,0x3d ,0x27 ,0x74 ,0x27 ,0x3e ,0x54 ,0xe1 ,0xba ,0xaf ,0x74 ,0x20 ,0x31 ,0x3c ,0x2f ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x3e ,0x3c ,0x2f ,0x61 ,0x3e ,0x3c ,0x74 ,0x64 ,0x3e ,0x0a ,0x09 ,0x20 ,0x20 ,0x20 ,0x20 ,0x3c ,0x74 ,0x72 ,0x3e ,0x0a ,0x09 ,0x20 ,0x20 ,0x20 ,0x20 ,0x3c ,0x74 ,0x72 ,0x3e ,0x0a ,0x09 ,0x09 ,0x09 ,0x3c ,0x74 ,0x64 ,0x3e ,0x3c ,0x61 ,0x20 ,0x68 ,0x72 ,0x65 ,0x66 ,0x3d ,0x27 ,0x2f ,0x62 ,0x61 ,0x74 ,0x32 ,0x27 ,0x3e ,0x3c ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x20 ,0x63 ,0x6c ,0x61 ,0x73 ,0x73 ,0x3d ,0x27 ,0x62 ,0x27 ,0x3e ,0x42 ,0xe1 ,0xba ,0xad ,0x74 ,0x20 ,0x32 ,0x3c ,0x2f ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x3e ,0x3c ,0x2f ,0x61 ,0x3e ,0x3c ,0x74 ,0x64 ,0x3e ,0x0a ,0x09 ,0x09 ,0x09 ,0x3c ,0x74 ,0x64 ,0x3e ,0x3c ,0x61 ,0x20 ,0x68 ,0x72 ,0x65 ,0x66 ,0x3d ,0x27 ,0x2f ,0x74 ,0x61 ,0x74 ,0x32 ,0x27 ,0x3e ,0x3c ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x20 ,0x63 ,0x6c ,0x61 ,0x73 ,0x73 ,0x3d ,0x27 ,0x74 ,0x27 ,0x3e ,0x54 ,0xe1 ,0xba ,0xaf ,0x74 ,0x20 ,0x32 ,0x3c ,0x2f ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x3e ,0x3c ,0x2f ,0x61 ,0x3e ,0x3c ,0x74 ,0x64 ,0x3e ,0x0a ,0x09 ,0x20 ,0x20 ,0x20 ,0x20 ,0x3c ,0x74 ,0x72 ,0x3e ,0x0a ,0x09 ,0x20 ,0x20 ,0x20 ,0x20 ,0x3c ,0x74 ,0x72 ,0x3e ,0x0a ,0x09 ,0x09 ,0x09 ,0x3c ,0x74 ,0x64 ,0x3e ,0x3c ,0x61 ,0x20 ,0x68 ,0x72 ,0x65 ,0x66 ,0x3d ,0x27 ,0x2f ,0x62 ,0x61 ,0x74 ,0x33 ,0x27 ,0x3e ,0x3c ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x20 ,0x63 ,0x6c ,0x61 ,0x73 ,0x73 ,0x3d ,0x27 ,0x62 ,0x27 ,0x3e ,0x42 ,0xe1 ,0xba ,0xad ,0x74 ,0x20 ,0x33 ,0x3c ,0x2f ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x3e ,0x3c ,0x2f ,0x61 ,0x3e ,0x3c ,0x74 ,0x64 ,0x3e ,0x0a ,0x09 ,0x09 ,0x09 ,0x3c ,0x74 ,0x64 ,0x3e ,0x3c ,0x61 ,0x20 ,0x68 ,0x72 ,0x65 ,0x66 ,0x3d ,0x27 ,0x2f ,0x74 ,0x61 ,0x74 ,0x33 ,0x27 ,0x3e ,0x3c ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x20 ,0x63 ,0x6c ,0x61 ,0x73 ,0x73 ,0x3d ,0x27 ,0x74 ,0x27 ,0x3e ,0x54 ,0xe1 ,0xba ,0xaf ,0x74 ,0x20 ,0x33 ,0x3c ,0x2f ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x3e ,0x3c ,0x2f ,0x61 ,0x3e ,0x3c ,0x74 ,0x64 ,0x3e ,0x0a ,0x09 ,0x20 ,0x20 ,0x20 ,0x20 ,0x3c ,0x74 ,0x72 ,0x3e ,0x0a ,0x09 ,0x20 ,0x20 ,0x20 ,0x20 ,0x3c ,0x74 ,0x72 ,0x3e ,0x0a ,0x09 ,0x09 ,0x09 ,0x3c ,0x74 ,0x64 ,0x3e ,0x3c ,0x61 ,0x20 ,0x68 ,0x72 ,0x65 ,0x66 ,0x3d ,0x27 ,0x2f ,0x62 ,0x61 ,0x74 ,0x34 ,0x27 ,0x3e ,0x3c ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x20 ,0x63 ,0x6c ,0x61 ,0x73 ,0x73 ,0x3d ,0x27 ,0x62 ,0x27 ,0x3e ,0x42 ,0xe1 ,0xba ,0xad ,0x74 ,0x20 ,0x34 ,0x3c ,0x2f ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x3e ,0x3c ,0x2f ,0x61 ,0x3e ,0x3c ,0x74 ,0x64 ,0x3e ,0x0a ,0x09 ,0x09 ,0x09 ,0x3c ,0x74 ,0x64 ,0x3e ,0x3c ,0x61 ,0x20 ,0x68 ,0x72 ,0x65 ,0x66 ,0x3d ,0x27 ,0x2f ,0x74 ,0x61 ,0x74 ,0x34 ,0x27 ,0x3e ,0x3c ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x20 ,0x63 ,0x6c ,0x61 ,0x73 ,0x73 ,0x3d ,0x27 ,0x74 ,0x27 ,0x3e ,0x54 ,0xe1 ,0xba ,0xaf ,0x74 ,0x20 ,0x34 ,0x3c ,0x2f ,0x62 ,0x75 ,0x74 ,0x74 ,0x6f ,0x6e ,0x3e ,0x3c ,0x2f ,0x61 ,0x3e ,0x3c ,0x74 ,0x64 ,0x3e ,0x0a ,0x09 ,0x20 ,0x20 ,0x20 ,0x20 ,0x3c ,0x74 ,0x72 ,0x3e ,0x09 ,0x0a ,0x09 ,0x3c ,0x2f ,0x74 ,0x61 ,0x62 ,0x6c ,0x65 ,0x3e ,0x0a ,0x3c ,0x2f ,0x64 ,0x69 ,0x76 ,0x3e ,0x0a ,0x3c ,0x2f ,0x62 ,0x6f ,0x64 ,0x79 ,0x3e ,0x0a ,0x3c ,0x2f ,0x68 ,0x74 ,0x6d ,0x6c ,0x3e }; |
Nếu không có nhu cầu xài tiếng việt thì không cần chuyển sang dạng HEX như này đâu
Giờ sẽ là công việc khó khăn hơn, viết code để đẩy cục data này đi khi có lệnh GET từ browser
Do phần header bắt đầu từ 0 đến trường IP->Urgent_Pointer là 54 byte, bộ đệm của chúng ta max là 512 byte nên mình còn dư 512-54 = 458 byte cho dữ liệu http. Nếu dữ liệu http > 458 thì phải cắt nhỏ ra gửi
Mình sẽ tạo 1 số biến toàn cục để lưu trữ số lượng gói tin đã gửi, đang gửi, trạng thái gửi, số hiệu gói tin vừa gửi trong http.c
1 2 3 |
uint8_t status=0; uint32_t data_num=0; uint32_t Sequence_Number=0; |
Đồng thời viết luôn 1 vài phương thức Set Get để các thư viện khác có thê truy cập vào các biến này
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
uint8_t HTTP_get_status(void) { return status; } void HTTP_set_status(uint8_t s) { status=s; } uint32_t HTTP_get_Sequence_Number(void) { return Sequence_Number; } void HTTP_set_Sequence_Number(uint32_t SequenceN) { Sequence_Number = SequenceN; } void HTTP_pack_init(void) { status=1; //bat dau gui goi tin http dau tien data_num=0; } |
Đừng quên thêm nguyên mẫu hàm cho nó vào file html.h nhé
Chỉnh lại code ở bài 13 một chút cho gọn
Mình sẽ tách 1 số đoạn code thiết lập struct tcp sang 1 hàm riêng cho gọn hàm TCP_read. Cụ thể, tạo thêm 1 hàm tên là TCP_make_herder trong file tcp.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
void TCP_make_herder(TCP_struct *TCP_Struct_Frame,uint16_t len,uint8_t type) { uint32_t dat_ack; uint16_t port,datalength,i; if(type == FOR_SYN) { //reply voi SYN|ACK //make reply memcpy(TCP_Struct_Frame->MAC_dich,TCP_Struct_Frame->MAC_nguon,6); memcpy(TCP_Struct_Frame->MAC_nguon,macaddr,6); TCP_Struct_Frame->CheckSum=0; memcpy(TCP_Struct_Frame->DestIP,TCP_Struct_Frame->SourceIP,4); //hoan vi source, dest memcpy(TCP_Struct_Frame->SourceIP,ip,4); //ip cua minh port = TCP_Struct_Frame->Source_Port; TCP_Struct_Frame->Source_Port = TCP_Struct_Frame->Dest_Port; TCP_Struct_Frame->Dest_Port = port; TCP_Struct_Frame->Acknowledgement = swap32(swap32(TCP_Struct_Frame->Sequence_Number) + 1); TCP_Struct_Frame->Sequence_Number = swap32(2071998); TCP_Struct_Frame->TCP_Flags = TCP_SYN | TCP_ACK; TCP_Struct_Frame->TCP_Checksums=0; TCP_Struct_Frame->Urgent_Pointer=0; TCP_Struct_Frame->CheckSum = NET_ipchecksum((uint8_t *)&TCP_Struct_Frame->Header_length); //tinh checksum cho goi IO TCP_Struct_Frame->TCP_Checksums = TCP_checksum(TCP_Struct_Frame); } else if(type == FOR_FIN) { //reply voi ACK //make reply memcpy(TCP_Struct_Frame->MAC_dich,TCP_Struct_Frame->MAC_nguon,6); memcpy(TCP_Struct_Frame->MAC_nguon,macaddr,6); TCP_Struct_Frame->CheckSum=0; memcpy(TCP_Struct_Frame->DestIP,TCP_Struct_Frame->SourceIP,4); //hoan vi source, dest memcpy(TCP_Struct_Frame->SourceIP,ip,4); //ip cua minh port = TCP_Struct_Frame->Source_Port; TCP_Struct_Frame->Source_Port = TCP_Struct_Frame->Dest_Port; TCP_Struct_Frame->Dest_Port = port; dat_ack = TCP_Struct_Frame->Acknowledgement; TCP_Struct_Frame->Acknowledgement = swap32(swap32(TCP_Struct_Frame->Sequence_Number) + 1); TCP_Struct_Frame->Sequence_Number = dat_ack; TCP_Struct_Frame->TCP_Flags = TCP_ACK; TCP_Struct_Frame->TCP_Checksums=0; TCP_Struct_Frame->Urgent_Pointer=0; TCP_Struct_Frame->CheckSum = NET_ipchecksum((uint8_t *)&TCP_Struct_Frame->Header_length); //tinh checksum cho goi IO TCP_Struct_Frame->TCP_Checksums = TCP_checksum(TCP_Struct_Frame); } else if(type == FOR_PSH_ACK) { //tinh do dai cua goi data va in ra man hinh datalength= swap16(TCP_Struct_Frame->TotoLength) -20 - (TCP_Struct_Frame->data_offset >> 2); // ( >> 4)*4 = >> 2 for(i=0;i<datalength;i++) UART_putChar(TCP_Struct_Frame->data[i]); //make reply len-=datalength; //resize len memcpy(TCP_Struct_Frame->MAC_dich,TCP_Struct_Frame->MAC_nguon,6); memcpy(TCP_Struct_Frame->MAC_nguon,macaddr,6); TCP_Struct_Frame->CheckSum=0; TCP_Struct_Frame->TotoLength = swap16(40); //defaul totolength TCP_Struct_Frame->data_offset = 0x50; //defaul data_offset memcpy(TCP_Struct_Frame->DestIP,TCP_Struct_Frame->SourceIP,4); //hoan vi source, dest memcpy(TCP_Struct_Frame->SourceIP,ip,4); //ip cua minh port = TCP_Struct_Frame->Source_Port; TCP_Struct_Frame->Source_Port = TCP_Struct_Frame->Dest_Port; TCP_Struct_Frame->Dest_Port = port; dat_ack = TCP_Struct_Frame->Acknowledgement; TCP_Struct_Frame->Acknowledgement = swap32(swap32(TCP_Struct_Frame->Sequence_Number) + datalength); TCP_Struct_Frame->Sequence_Number = dat_ack; TCP_Struct_Frame->TCP_Flags = TCP_ACK; TCP_Struct_Frame->TCP_Checksums=0; TCP_Struct_Frame->Urgent_Pointer=0; TCP_Struct_Frame->CheckSum = NET_ipchecksum((uint8_t *)&TCP_Struct_Frame->Header_length); //tinh checksum cho goi IO TCP_Struct_Frame->TCP_Checksums = TCP_checksum(TCP_Struct_Frame); } } |
Và định nghĩa thêm các tham số của đối số type vào file tcp.h
1 2 3 |
#define FOR_SYN 0 #define FOR_FIN 1 #define FOR_PSH_ACK 2 |
Hàm TCP_make_herder sẽ kiểm tra tham số type để xử lí Struct tcp cho phù hợp
Bây giờ trong hàm TCP_read, chúng ta có thể viết gọn lại bằng cách gọi hàm TCP_make_herder. Đây là hàm TCP_read gọn nhẹ !
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
void TCP_read(uint8_t *TCP_Frame,uint16_t len) { TCP_struct *TCP_Struct_Frame = (TCP_struct *)TCP_Frame; //kiem tra dia chi ip xem co phai no gui cho minh khong if( memcmp(TCP_Struct_Frame->DestIP,ip,4) )return; // dung memcmp de so sanh, neu khac thi thoat if(TCP_Struct_Frame->TCP_Flags == TCP_SYN) { TCP_make_herder(TCP_Struct_Frame,len,FOR_SYN); NET_SendFrame((uint8_t *)TCP_Struct_Frame,len); //gui goi tin tcp reply } else if(TCP_Struct_Frame->TCP_Flags == TCP_ACK) { UART_putString("Ack\r\n"); } else if(TCP_Struct_Frame->TCP_Flags == (TCP_FIN|TCP_ACK)) { TCP_make_herder(TCP_Struct_Frame,len,FOR_FIN); NET_SendFrame((uint8_t *)TCP_Struct_Frame,len); //gui goi tin tcp reply TCP_Struct_Frame->TCP_Flags = TCP_FIN|TCP_ACK; TCP_Struct_Frame->TCP_Checksums=0; TCP_Struct_Frame->TCP_Checksums = TCP_checksum(TCP_Struct_Frame); NET_SendFrame((uint8_t *)TCP_Struct_Frame,len); //gui goi tin tcp reply } else if(TCP_Struct_Frame->TCP_Flags == (TCP_PSH|TCP_ACK)) { TCP_make_herder(TCP_Struct_Frame,len,FOR_PSH_ACK); if (strncmp((char*)TCP_Struct_Frame->data,"GET /", 5) == 0) { //có 1 request gửi tới kìa } } } |
OK ! Bây giờ chúng ta sẽ bắt đầu gửi trả nội dung trang web cho client khi có 1 request gửi tới
Do trang web của chúng ta khá dài nên sẽ phải chia thành nhiều gói nhỏ để gửi. Khi gửi nhiều gói tin :
Phương pháp đơn giản nhất là : Khi server trả về cho client 1 gói tin, server sẽ chờ nhận được phản hồi ACK của client để gửi tiếp, mà cái phản hồi ACK đấy phải chính xác là của gói tin mà server đã gửi. Chúng ta sẽ check Sequence Number (số hiệu nhận dạng gói tin) xem có trùng với Sequence Number lúc gửi đi không ! Nếu trùng thì có thể chắc chắn 100% client đã nhận được chính xác gói tin mà server đã gửi cho nó. Lúc này chúng ta mới gửi gói tin tiếp theo!
Thực ra đáng lẽ còn phải có 1 trường hợp nữa là server sẽ làm gì khi chờ mãi mà không nhận được ACK phản hồi, nhưng nó sẽ làm phức tạp hóa vấn đề lên rất nhiều. Chúng ta sẽ tạm bỏ qua các vấn đề này và xử lí nó khi làm việc với phương pháp lập trình non-blocking
Mình tạo thêm biến status trong thư viện http.c và sẽ sử dụng nó với mục đích là kiểm tra xem server có đang “bận” trả lời request GET cho thằng nào không. Nếu == 1 thì là đang bận rồi, thằng nào đang GET thì chờ chút đê. Nếu == 0 thì là đang rảnh, có thể phản hồi ngay
Sử lại hàm TCP_send 1 chút
Ở hàm cũ thì mình chỉ nhận dữ liệu và tự tính toán độ dài dữ liệu trong hàm TCP_send
Mình sẽ sửa lại thành nhận dữ liệu kèm cả độ dài của dữ liệu và thêm 1 cái offset để căn chỉnh sữ liệu cho tiện. Bởi vì data web của chúng ta lưu vào bộ nhớ flash của chíp atmega328, mà truy cập flash ở AVR có hơi vất vả hơn các dòng chip khác 1 chút.
Đây là hàm TCP_send viết lại
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void TCP_send(TCP_struct *TCP_Struct_Frame,uint16_t len,flash uint8_t *data,uint16_t data_length) { uint16_t i; for(i=0;i<data_length;i++)TCP_Struct_Frame->data[i] = data[i]; len+=data_length; TCP_Struct_Frame->TotoLength = swap16(swap16(TCP_Struct_Frame->TotoLength) + data_length); //make totolength TCP_Struct_Frame->CheckSum=0; TCP_Struct_Frame->TCP_Checksums=0; TCP_Struct_Frame->TCP_Flags = TCP_PSH|TCP_ACK; TCP_Struct_Frame->CheckSum = NET_ipchecksum((uint8_t *)&TCP_Struct_Frame->Header_length); //tinh checksum cho goi IO TCP_Struct_Frame->TCP_Checksums = TCP_checksum(TCP_Struct_Frame); NET_SendFrame((uint8_t *)TCP_Struct_Frame,len); } |
Như vậy khi gọi hàm TCP_send chúng ta phải nhập vào thêm 2 thông tin đó là:
- data : Đây là 1 con trỏ dạng flash trỏ tới nơi lưu trữ nội dung web được lưu ở bộ nhớ flash
- data_length : Độ dài của data sẽ gửi đi là bao nhiêu ( 0 -> 458)
Thư viện http sẽ có nhiệm vụ cung cấp cho bên tcp biết 2 thông số này, vì vậy mình sẽ viết 1 hàm làm việc đó
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
uint8_t HTTP_get_data_num(flash uint8_t** data,uint16_t* data_length) { if(data_num >= sizeof(web_page)) //da gui xong data, dong ket noi { UART_putString("da gui xong tap tin html\r\n"); return 1;//end } else if( (sizeof(web_page) - data_num) > 458 ) //neu du lieu can gui > 458 (con gui tiep nua) *data_length=458; else if( (sizeof(web_page) - data_num) <= 458 ) //neu du lieu can gui <= 458 (gui lan cuoi) *data_length = sizeof(web_page) - data_num; *data=web_page+data_num; data_num+=*data_length; return 0; } |
Ngoài việc cung cấp 2 thông số data và data_length qua phương pháp truy cập gián tiếp qua con trỏ ( đối với data_length) và thông qua con trỏ 2 cấp ( đối với data ở dạng flash). Hàm HTTP_get_data_num còn trả về 1 – tức dữ liệu gửi đi hết rồi, và 0 – vẫn còn dữ liệu để gửi tiếp
<tại hàm TCP_read> chỗ //có 1 request gửi tới kìa
Chúng ta sẽ bắt đầu trả lời gói tin đầu tiên ngay tại đây
1 2 3 4 5 6 7 8 9 10 |
if(HTTP_get_status() != 1) { HTTP_pack_init(); //bat dau gui 1 goi tin TCP_make_herder(TCP_Struct_Frame,len,FOR_PSH_ACK); if(HTTP_get_data_num(&data_send,&data_send_len) == 0) { TCP_send(TCP_Struct_Frame,len,data_send,data_send_len); HTTP_set_Sequence_Number(TCP_Struct_Frame->Acknowledgement); //luu so hieu goi tin da gui lai } } |
Các gói tin tiếp theo sẽ được gửi khi chúng ta nhận được cờ ACK
<Tại chỗ nhận cờ ACK trong hàm TCP_read>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
if(HTTP_get_status()==1 ) //neu dang co nhiem vu gui cac goi tin http { if(TCP_Struct_Frame->Sequence_Number==HTTP_get_Sequence_Number()) //neu goi tin truoc da gui thanh cong { TCP_make_herder(TCP_Struct_Frame,len,FOR_PSH_ACK); if(HTTP_get_data_num(&data_send,&data_send_len) == 0) { TCP_send(TCP_Struct_Frame,len,data_send,data_send_len); HTTP_set_Sequence_Number(TCP_Struct_Frame->Acknowledgement); //luu so hieu goi tin da gui lai } else { } } } |
Tổng quan, toàn bộ hàm TCP_read lúc này sẽ như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
void TCP_read(uint8_t *TCP_Frame,uint16_t len) { flash uint8_t *data_send=0; uint16_t data_send_len=0; TCP_struct *TCP_Struct_Frame = (TCP_struct *)TCP_Frame; //kiem tra dia chi ip xem co phai no gui cho minh khong if( memcmp(TCP_Struct_Frame->DestIP,ip,4) )return; // dung memcmp de so sanh, neu khac thi thoat if(TCP_Struct_Frame->TCP_Flags == TCP_SYN) { TCP_make_herder(TCP_Struct_Frame,len,FOR_SYN); NET_SendFrame((uint8_t *)TCP_Struct_Frame,len); //gui goi tin tcp reply } else if(TCP_Struct_Frame->TCP_Flags == TCP_ACK) { UART_putString("Ack\r\n"); if(HTTP_get_status()==1 ) //neu dang co nhiem vu gui cac goi tin http { if(TCP_Struct_Frame->Sequence_Number==HTTP_get_Sequence_Number()) //neu goi tin truoc da gui thanh cong { TCP_make_herder(TCP_Struct_Frame,len,FOR_PSH_ACK); if(HTTP_get_data_num(&data_send,&data_send_len) == 0) { TCP_send(TCP_Struct_Frame,len,data_send,data_send_len); HTTP_set_Sequence_Number(TCP_Struct_Frame->Acknowledgement); //luu so hieu goi tin da gui lai } else { //gửi hết rồi,không còn gì để gửi nữa } } } } else if(TCP_Struct_Frame->TCP_Flags == (TCP_FIN|TCP_ACK)) { TCP_make_herder(TCP_Struct_Frame,len,FOR_FIN); NET_SendFrame((uint8_t *)TCP_Struct_Frame,len); //gui goi tin tcp reply TCP_Struct_Frame->TCP_Flags = TCP_FIN|TCP_ACK; TCP_Struct_Frame->TCP_Checksums=0; TCP_Struct_Frame->TCP_Checksums = TCP_checksum(TCP_Struct_Frame); NET_SendFrame((uint8_t *)TCP_Struct_Frame,len); //gui goi tin tcp reply } else if(TCP_Struct_Frame->TCP_Flags == (TCP_PSH|TCP_ACK)) { TCP_make_herder(TCP_Struct_Frame,len,FOR_PSH_ACK); if (strncmp((char*)TCP_Struct_Frame->data,"GET /", 5) == 0) { if(HTTP_get_status() != 1) { HTTP_pack_init(); //bat dau gui 1 goi tin if(HTTP_get_data_num(&data_send,&data_send_len) == 0) { TCP_send(TCP_Struct_Frame,len,data_send,data_send_len); HTTP_set_Sequence_Number(TCP_Struct_Frame->Acknowledgement); //luu so hieu goi tin da gui lai } } } } } |
Vất vả rồi 🙁 chạy mô phỏng và test thử thôi
Pơ phệch ! Web server của chúng ta đã hiện nguyên hình
Chúng ta sẽ hoàn thành nốt công việc đóng kết nối với client (thanh loadding sẽ ngừng quay) và gọi hàm xóa biến status về 0 để báo server rảnh, sẵn sàng phản hồi các truy vấn khác
tại chỗ //gửi hết rồi,không còn gì để gửi nữa
1 2 3 4 5 6 7 |
HTTP_set_status(0); //dong ket noi TCP_Struct_Frame->TCP_Flags = TCP_FIN|TCP_ACK; TCP_Struct_Frame->TCP_Checksums=0; TCP_Struct_Frame->TCP_Checksums = TCP_checksum(TCP_Struct_Frame); NET_SendFrame((uint8_t *)TCP_Struct_Frame,len); //gui goi tin ngat ket noi |
Chạy thử
OK rồi ! Trình duyệt web đã ngừng loadding sau khi đã nhận xong data !
Các bạn có thể xóa phần in dữ liệu debug ra màn hình trong hàm TCP_make_herder để chương trình phản hồi nhanh hơn (nhanh hơn hẳn luôn @@)
Xóa luôn máy dòng debug trong thư viện http
Nói chung chỗ nào có chạy hàm UART thì xóa đi để web load ở tốc độ bàn thờ
Điều khiển thiết bị
Tiếp theo là tiết mục điều khiển thiết bị thần thánh
Trước lệnh kiểm tra “GET / ” trong hàm TCP_read chỗ nhận cờ TCP_PSH|TCP_ACK, các bạn thêm vài dòng lệnh so sánh chuỗi rồi bật tắt thiết bị là được.
1 2 3 4 5 6 7 8 |
if(strstr(TCP_Struct_Frame->data,"bat1"))PORTC.0=0; else if(strstr(TCP_Struct_Frame->data,"tat1"))PORTC.0=1; else if(strstr(TCP_Struct_Frame->data,"bat2"))PORTC.1=0; else if(strstr(TCP_Struct_Frame->data,"tat2"))PORTC.1=1; else if(strstr(TCP_Struct_Frame->data,"bat3"))PORTC.2=0; else if(strstr(TCP_Struct_Frame->data,"tat3"))PORTC.2=1; else if(strstr(TCP_Struct_Frame->data,"bat4"))PORTC.3=0; else if(strstr(TCP_Struct_Frame->data,"tat4"))PORTC.3=1; |
Download
Tải full source bài 14 tại đây
Full tutorial ENC28J60: http://iot47.com/category/iot-tutorial/ethernetgiao-tiep-enc28j60/
Full tutorila IoT – ESP8266: http://iot47.com/category/iot-tutorial/giao-tiep-esp8266/
Full tutorial lập trình led ma trận: http://iot47.com/category/ma-tran-led/