kopia lustrzana https://github.com/pimoroni/pimoroni-pico
Add RGB HTTP demo
rodzic
2e3af3b436
commit
306877cdf5
|
|
@ -6,6 +6,12 @@ namespace pimoroni {
|
||||||
addr.dword = 0;
|
addr.dword = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string IPAddress::to_string() {
|
||||||
|
char buf[16] = {0};
|
||||||
|
snprintf(buf, 16, "%d.%d.%d.%d", addr.bytes[0], addr.bytes[1], addr.bytes[2], addr.bytes[3]);
|
||||||
|
return std::string(buf, 16);
|
||||||
|
}
|
||||||
|
|
||||||
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) {
|
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet) {
|
||||||
addr.bytes[0] = first_octet;
|
addr.bytes[0] = first_octet;
|
||||||
addr.bytes[1] = second_octet;
|
addr.bytes[1] = second_octet;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "pico/stdlib.h"
|
#include "pico/stdlib.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace pimoroni {
|
namespace pimoroni {
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ namespace pimoroni {
|
||||||
uint8_t& operator[](int index);
|
uint8_t& operator[](int index);
|
||||||
IPAddress& operator=(uint32_t address);
|
IPAddress& operator=(uint32_t address);
|
||||||
operator uint32_t() const;
|
operator uint32_t() const;
|
||||||
|
std::string to_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,14 +1,2 @@
|
||||||
add_executable(
|
include("${CMAKE_CURRENT_LIST_DIR}/demo.cmake")
|
||||||
wireless_time
|
include("${CMAKE_CURRENT_LIST_DIR}/rgb_http.cmake")
|
||||||
demo.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# enable usb output, disable uart output
|
|
||||||
pico_enable_stdio_usb(wireless_time 1)
|
|
||||||
pico_enable_stdio_uart(wireless_time 0)
|
|
||||||
|
|
||||||
# Pull in pico libraries that we need
|
|
||||||
target_link_libraries(wireless_time pico_stdlib pico_wireless)
|
|
||||||
|
|
||||||
# create map/bin/hex file etc.
|
|
||||||
pico_add_extra_outputs(wireless_time)
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
add_executable(
|
||||||
|
wireless_time
|
||||||
|
demo.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# enable usb output, disable uart output
|
||||||
|
pico_enable_stdio_usb(wireless_time 1)
|
||||||
|
pico_enable_stdio_uart(wireless_time 0)
|
||||||
|
|
||||||
|
# Pull in pico libraries that we need
|
||||||
|
target_link_libraries(wireless_time pico_stdlib pico_wireless)
|
||||||
|
|
||||||
|
# create map/bin/hex file etc.
|
||||||
|
pico_add_extra_outputs(wireless_time)
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
add_executable(
|
||||||
|
wireless_rgb_http
|
||||||
|
rgb_http.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# enable usb output, enable uart output
|
||||||
|
pico_enable_stdio_usb(wireless_rgb_http 1)
|
||||||
|
pico_enable_stdio_uart(wireless_rgb_http 1)
|
||||||
|
|
||||||
|
# Pull in pico libraries that we need
|
||||||
|
target_link_libraries(wireless_rgb_http pico_stdlib pico_wireless)
|
||||||
|
|
||||||
|
# create map/bin/hex file etc.
|
||||||
|
pico_add_extra_outputs(wireless_rgb_http)
|
||||||
|
|
@ -0,0 +1,244 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "pico/stdlib.h"
|
||||||
|
#include "hardware/uart.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
#include "hardware/spi.h"
|
||||||
|
#include "pico_wireless.hpp"
|
||||||
|
#include "secrets.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#define HTTP_PORT 80
|
||||||
|
#define HTTP_REQUEST_BUF_SIZE 2048
|
||||||
|
|
||||||
|
#define DNS_CLOUDFLARE IPAddress(1, 1, 1, 1)
|
||||||
|
#define DNS_GOOGLE IPAddress(8, 8, 8, 8)
|
||||||
|
#define USE_DNS DNS_CLOUDFLARE
|
||||||
|
|
||||||
|
using namespace pimoroni;
|
||||||
|
|
||||||
|
PicoWireless wireless;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
uint8_t request_buf[HTTP_REQUEST_BUF_SIZE];
|
||||||
|
const std::string response_501 = "HTTP/1.1 501 Not Implemented\nContent-Length: 19\n\n501 Not Implemented";
|
||||||
|
|
||||||
|
enum http_request_method_t {
|
||||||
|
GET,
|
||||||
|
POST
|
||||||
|
};
|
||||||
|
typedef std::string_view(*http_request_handler)(http_request_method_t method, std::string_view path, std::vector<std::string_view> request_head, std::vector<std::string_view> request_body);
|
||||||
|
|
||||||
|
std::vector<std::string_view> split(std::string_view str, std::string delim="\r\n") {
|
||||||
|
std::vector<std::string_view> result;
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < str.size()) {
|
||||||
|
const auto pos = str.find_first_of(delim, offset);
|
||||||
|
// Emit an empty view even if two adjacent delimiters are found
|
||||||
|
// this ensurs the HTTP "blank line" start of content is found
|
||||||
|
result.emplace_back(str.substr(offset, pos - offset));
|
||||||
|
if (pos == std::string_view::npos) break;
|
||||||
|
offset = pos + delim.length();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t millis() {
|
||||||
|
return to_us_since_boot(get_absolute_time()) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wifi_connect(std::string network, std::string password, IPAddress dns_server, uint32_t timeout=10000) {
|
||||||
|
printf("Connecting to %s...\n", network.c_str());
|
||||||
|
wireless.wifi_set_passphrase(network, password);
|
||||||
|
|
||||||
|
uint32_t t_start = millis();
|
||||||
|
|
||||||
|
while(millis() - t_start < timeout) {
|
||||||
|
if(wireless.get_connection_status() == WL_CONNECTED) {
|
||||||
|
printf("Connected!\n");
|
||||||
|
wireless.set_dns(1, dns_server, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
wireless.set_led(255, 0, 0);
|
||||||
|
sleep_ms(500);
|
||||||
|
wireless.set_led(0, 0, 0);
|
||||||
|
sleep_ms(500);
|
||||||
|
printf("...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int socket_accept(int8_t server_sock) {
|
||||||
|
uint8_t new_sock = wireless.avail_server(server_sock);
|
||||||
|
if(new_sock != server_sock
|
||||||
|
&& new_sock != 255) {
|
||||||
|
return new_sock;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t start_server(uint16_t http_port) {
|
||||||
|
printf("Starting server...\n", NETWORK);
|
||||||
|
// Get a socket for our server
|
||||||
|
uint8_t server_sock = wireless.get_socket();
|
||||||
|
wireless.start_server(http_port, server_sock);
|
||||||
|
while(1){
|
||||||
|
uint8_t state = wireless.get_server_state(server_sock);
|
||||||
|
if(state == LISTEN) break;
|
||||||
|
}
|
||||||
|
IPAddress ip;
|
||||||
|
wireless.get_ip_address(ip);
|
||||||
|
printf("Server listening on %s:%i\n", ip.to_string().c_str(), http_port);
|
||||||
|
return server_sock;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool poll_http_requests(uint8_t server_sock, http_request_handler request_handler, uint32_t timeout = 1000) {
|
||||||
|
uint16_t request_length = 0;
|
||||||
|
uint16_t avail_length = 0;
|
||||||
|
uint32_t t_start = millis();
|
||||||
|
|
||||||
|
// Clear the request buffer
|
||||||
|
memset(request_buf, 0, HTTP_REQUEST_BUF_SIZE);
|
||||||
|
|
||||||
|
// Try to accept an incoming connection
|
||||||
|
int client_sock = socket_accept(server_sock);
|
||||||
|
if(client_sock == -1) return false;
|
||||||
|
|
||||||
|
printf("Got client: %d\n", client_sock);
|
||||||
|
|
||||||
|
// Any data? This *usually* seems to be available immediately,
|
||||||
|
// so we can dispense with any ongoing polling and hope for the best.
|
||||||
|
avail_length = wireless.avail_data(client_sock);
|
||||||
|
if(avail_length == 0) return false;
|
||||||
|
|
||||||
|
printf("Got request: %i bytes\n", avail_length);
|
||||||
|
|
||||||
|
// Read the full response
|
||||||
|
// Sometimes the bytes read is less than the bytes we request, so loop until we get the data we expect
|
||||||
|
while(request_length < avail_length) {
|
||||||
|
uint16_t read_length = avail_length; // Request the full buffer
|
||||||
|
wireless.get_data_buf(client_sock, request_buf + request_length, &read_length);
|
||||||
|
request_length += read_length; // Increment the response_length by the amount we actually read
|
||||||
|
|
||||||
|
// Also check for timeouts here, too
|
||||||
|
if(millis() - t_start >= timeout) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bail if we timed out.
|
||||||
|
if(millis() - t_start >= timeout) {
|
||||||
|
wireless.stop_client(client_sock);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(request_length > 0) {
|
||||||
|
std::vector<std::string_view> request = split(std::string_view((char *)request_buf, request_length));
|
||||||
|
std::vector<std::string_view> request_body;
|
||||||
|
std::vector<std::string_view> request_detail = split(request[0], " ");
|
||||||
|
http_request_method_t method;
|
||||||
|
|
||||||
|
// Bail early on an invalid HTTP request
|
||||||
|
if(request_detail[0].compare("POST") == 0) {
|
||||||
|
method = POST;
|
||||||
|
} else if (request_detail[0].compare("GET") == 0) {
|
||||||
|
method = GET;
|
||||||
|
} else { // PUT, DELETE, HEAD?
|
||||||
|
// Return a 501 unimplemented
|
||||||
|
wireless.send_data(client_sock, (const uint8_t *)response_501.data(), response_501.length());
|
||||||
|
wireless.stop_client(client_sock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the request path so we can handle routes
|
||||||
|
std::string_view path = request_detail[1];
|
||||||
|
|
||||||
|
// Scan for the blank line indicating content start for form post data
|
||||||
|
auto body_start = std::find(request.begin(), request.end(), "");
|
||||||
|
|
||||||
|
// Split the body from the head (ow!)
|
||||||
|
if(body_start != request.end()) {
|
||||||
|
request_body = std::vector<std::string_view>(body_start + 1, request.end());
|
||||||
|
request = std::vector<std::string_view>(request.begin(), body_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view response_body = request_handler(method, path, request, request_body);
|
||||||
|
std::string response_head = "HTTP/1.1 200 OK\nContent-Length: " + std::to_string(response_body.length()) + "\nContent-Type: text/html\n\n";
|
||||||
|
wireless.send_data(client_sock, (const uint8_t *)response_head.data(), response_head.length());
|
||||||
|
wireless.send_data(client_sock, (const uint8_t *)response_body.data(), response_body.length());
|
||||||
|
wireless.stop_client(client_sock);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
stdio_init_all();
|
||||||
|
|
||||||
|
wireless.init();
|
||||||
|
sleep_ms(500);
|
||||||
|
|
||||||
|
printf("Firmware version Nina %s\n", wireless.get_fw_version());
|
||||||
|
|
||||||
|
if(!wifi_connect(NETWORK, PASSWORD, USE_DNS)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t server_sock = start_server(HTTP_PORT);
|
||||||
|
|
||||||
|
g = 255;
|
||||||
|
wireless.set_led(r, g, b);
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
// Handle any incoming HTTP requests
|
||||||
|
poll_http_requests(server_sock, [](
|
||||||
|
http_request_method_t request_method,
|
||||||
|
std::string_view request_path,
|
||||||
|
std::vector<std::string_view> request_head,
|
||||||
|
std::vector<std::string_view> request_body) -> std::string_view {
|
||||||
|
|
||||||
|
if(request_method == POST) {
|
||||||
|
// Split the URL-encoded POST data and parse the RGB values
|
||||||
|
std::vector<std::string_view> post_data = split(request_body[0], "&");
|
||||||
|
for(auto &data : post_data) {
|
||||||
|
// Must be at least 3 chars for "x=y"
|
||||||
|
if(data.length() < 3) continue;
|
||||||
|
// Must be in the form "x=y"
|
||||||
|
if(data[1] != '=') continue;
|
||||||
|
// "v" will be 0 on a parse failure, which isn't so bad.
|
||||||
|
int v = atoi((const char *)(data.data() + 2));
|
||||||
|
switch(data[0]) {
|
||||||
|
case 'r':
|
||||||
|
r = v;
|
||||||
|
printf("Got R: %i\n", r);
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
g = v;
|
||||||
|
printf("Got G: %i\n", g);
|
||||||
|
break;
|
||||||
|
case 'b':
|
||||||
|
b = v;
|
||||||
|
printf("Got B: %i\n", b);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
wireless.set_led(r, g, b);
|
||||||
|
}
|
||||||
|
} else { // GET
|
||||||
|
if(request_path.compare("/hello") == 0) {
|
||||||
|
return "Hello World!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "\
|
||||||
|
<form method=\"post\" action=\"/\">\
|
||||||
|
<input id=\"r\" name=\"r\" type=\"number\" value=\"" + std::to_string(r) + "\" />\
|
||||||
|
<input name=\"g\" type=\"number\" value=\"" + std::to_string(g) + "\" />\
|
||||||
|
<input name=\"b\" type=\"number\" value=\"" + std::to_string(b) + "\" />\
|
||||||
|
<input type=\"submit\" value=\"Set LED\" />\
|
||||||
|
</form>";
|
||||||
|
});
|
||||||
|
sleep_ms(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Ładowanie…
Reference in New Issue