Skip to content

Commit

Permalink
[WIP] Add M5Atom Car example
Browse files Browse the repository at this point in the history
  • Loading branch information
TwoSquirrels committed Jul 15, 2024
1 parent 7d4be49 commit 93cb46e
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 5 deletions.
3 changes: 3 additions & 0 deletions example/m5atom-car/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env.h
index.html
index-html.h
3 changes: 3 additions & 0 deletions example/m5atom-car/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# M5Atom WiFi Control Car Example

TODO: 説明を書く
133 changes: 133 additions & 0 deletions example/m5atom-car/SimpleHTTPServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
// SimpleHTTPServer written by ChatGPT

#include <WiFi.h>

#define MAX_HANDLERS (16)

class SimpleHTTPServer {
private:
struct Handler {
const char* path;
void (*handler)(WiFiClient&, const String&);
};

const char* ssid;
const char* password;
WiFiServer server;
Handler getHandlers[MAX_HANDLERS];
Handler postHandlers[MAX_HANDLERS];
int getHandlerCount;
int postHandlerCount;

public:
SimpleHTTPServer(const char* ssid, const char* password, int port = 80)
: ssid(ssid), password(password), server(port), getHandlerCount(0), postHandlerCount(0) {}

void begin() {
Serial.begin(115200);
connectToWiFi();
server.begin();
Serial.println("Server started");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void handleClient() {
WiFiClient client = server.available();
if (client) {
Serial.println("New Client.");
String currentLine = "";
String requestType = "";
String path = "";
String queryString = "";

while (client.connected()) {
if (client.available()) {
char c = client.read();
Serial.write(c);
if (c == '\n') {
if (currentLine.length() == 0) {
if (requestType == "GET") {
handleRequest(path, queryString, client, getHandlers, getHandlerCount);
} else if (requestType == "POST") {
handleRequest(path, queryString, client, postHandlers, postHandlerCount);
}
break;
} else {
currentLine = "";
}
} else if (c != '\r') {
currentLine += c;
if (currentLine.startsWith("GET ")) {
requestType = "GET";
path = extractPathAndQuery(currentLine, queryString);
} else if (currentLine.startsWith("POST ")) {
requestType = "POST";
path = extractPathAndQuery(currentLine, queryString);
}
}
}
}
client.stop();
Serial.println("Client Disconnected.");
}
}

void get(const char* path, void (*handler)(WiFiClient&, const String&)) {
if (getHandlerCount < MAX_HANDLERS) {
getHandlers[getHandlerCount++] = { path, handler };
}
}

void post(const char* path, void (*handler)(WiFiClient&, const String&)) {
if (postHandlerCount < MAX_HANDLERS) {
postHandlers[postHandlerCount++] = { path, handler };
}
}

private:
void connectToWiFi() {
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected.");
}

String extractPathAndQuery(const String& requestLine, String& queryString) {
int firstSpace = requestLine.indexOf(' ');
int secondSpace = requestLine.indexOf(' ', firstSpace + 1);
String fullPath = requestLine.substring(firstSpace + 1, secondSpace);

int queryIndex = fullPath.indexOf('?');
if (queryIndex != -1) {
queryString = fullPath.substring(queryIndex + 1);
return fullPath.substring(0, queryIndex);
} else {
queryString = "";
return fullPath;
}
}

void handleRequest(const String& path, const String& queryString, WiFiClient& client, Handler* handlers, int handlerCount) {
for (int i = 0; i < handlerCount; i++) {
if (path == handlers[i].path) {
handlers[i].handler(client, queryString);
return;
}
}
defaultResponse(client);
}

void defaultResponse(WiFiClient& client) {
client.println("HTTP/1.1 404 Not Found");
client.println("Content-type:text/html");
client.println();
client.print("<html><body><h1>404 Not Found</h1></body></html>");
client.println();
}
};
12 changes: 12 additions & 0 deletions example/m5atom-car/gen-index-html.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@echo off
setlocal EnableDelayedExpansion

powershell -Command "(New-Object Net.WebClient).DownloadFile('https://twosquirrels.github.io/virtual-gamepad/', 'index.html')"

(
echo const char index_html[] = R"***(
type index.html
echo )***";
) > index-html.h

echo index-html.h has been created successfully.
11 changes: 11 additions & 0 deletions example/m5atom-car/gen-index-html.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash

curl -o index.html https://twosquirrels.github.io/virtual-gamepad/

{
echo 'const char index_html[] = R"***('
cat index.html
echo ')***";'
} > index-html.h

echo "index-html.h has been created successfully."
84 changes: 84 additions & 0 deletions example/m5atom-car/m5atom-car.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// WiFi Control Car Example

#include "SimpleHTTPServer.h"
#include <M5Atom.h>

#define LEDright (22)
#define LEDleft (19)
#define MotorA (23)
#define MotorB (33)

const double PWM_freq = 2000; // PWM周波数.
const uint8_t PWM_res = 8; // PWM分解能 16bit(0~256).
const uint8_t PWM_CH_A = 1; // チャンネル.
const uint8_t PWM_CH_B = 2; // チャンネル.

#if __has_include(".env.h")
# include ".env.h"
#else
const char ssid[] = "YOUR_SSID";
const char password[] = "YOUR_PASSWORD";
#endif

#if __has_include("index-html.h")
# include "index-html.h"
#else
const char index_html[] = "<html><body><h1>There is not index-html.h</h1></body></html>";
#endif

SimpleHTTPServer server(ssid, password);

void handleRoot(WiFiClient& client, const String& query) {
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println();
client.println(index_html);
}

void respondOK(WiFiClient& client) {
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/plain");
client.println();
client.println("OK");
}

void handlePostA(WiFiClient& client, const String& query) {
respondOK(client);
Serial.println("======== Pushed A button! ========");
}

void handlePostB(WiFiClient& client, const String& query) {
respondOK(client);
Serial.println("======== Pushed B button! ========");
}

void setup() {
M5.begin(false, false, true);
Serial.begin(115200);
M5.dis.drawpix(0, 0x0000FF);
pinMode(LEDright, OUTPUT);
pinMode(LEDleft, OUTPUT);
pinMode(MotorA, OUTPUT);
pinMode(MotorB, OUTPUT);
//チャンネル1と周波数の分解能を設定.
ledcSetup(PWM_CH_A, PWM_freq, PWM_res);
//チャンネル2と周波数の分解能を設定.
ledcSetup(PWM_CH_B, PWM_freq, PWM_res);

//モータのピンとチャンネルの設定.
ledcAttachPin(MotorA, PWM_CH_A);
ledcAttachPin(MotorB, PWM_CH_B);

delay(10);

server.begin();

server.get("/", handleRoot);
server.post("/a", handlePostA);
server.post("/b", handlePostB);
}

void loop() {
server.handleClient();
M5.update();
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"preview": "vite preview"
},
"devDependencies": {
"vite": "^5.3.1"
"vite": "^5.3.1",
"vite-plugin-singlefile": "^2.0.2"
},
"dependencies": {
"nipplejs": "^0.10.2",
Expand Down
61 changes: 61 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 93cb46e

Please sign in to comment.