Hendrik Langer
7 years ago
12 changed files with 377 additions and 2 deletions
@ -0,0 +1,11 @@ |
|||
#ifndef _HARDWARE_H |
|||
#define _HARDWARE_H |
|||
|
|||
#define WIFI_SSID "ssid" |
|||
#define WIFI_PASSWORD "password" |
|||
|
|||
#define LED_BUILTIN 13 |
|||
|
|||
static constexpr int SERVO_PINS[] = {22, 23, 24, 25}; |
|||
|
|||
#endif /* _HARDWARE_H */ |
@ -0,0 +1,9 @@ |
|||
#include "led.h" |
|||
|
|||
using namespace std; |
|||
|
|||
Led::Led(void) { |
|||
} |
|||
|
|||
void Led::setup(void) { |
|||
} |
@ -0,0 +1,11 @@ |
|||
#ifndef _LED_H |
|||
#define _LED_H |
|||
|
|||
class Led { |
|||
public: |
|||
Led(void); |
|||
void setup(void); |
|||
private: |
|||
}; |
|||
|
|||
#endif /* _LED_H */ |
@ -0,0 +1,46 @@ |
|||
#include <Arduino.h> |
|||
#include "esp32-hal-ledc.h" |
|||
#include "semaphore.h" |
|||
|
|||
#include "hardware.h" |
|||
#include "pusher.h" |
|||
|
|||
using namespace std; |
|||
|
|||
Pusher::Pusher(void) { |
|||
xSemaphore = xSemaphoreCreateMutex(); |
|||
} |
|||
|
|||
void Pusher::setup(void) { |
|||
int channel = 1; |
|||
int pin = 22; |
|||
for(auto pin : SERVO_PINS) { |
|||
if (xSemaphoreTake(xSemaphore, TIMEOUT) == pdTRUE) { |
|||
ledcSetup(channel, 50, TIMER_WIDTH); |
|||
ledcAttachPin(pin, channel); |
|||
ledcWrite(channel, MAX_PULSE_WIDTH); |
|||
ledcDetachPin(pin); |
|||
xSemaphoreGive(xSemaphore); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void Pusher::dispense(int num) { |
|||
if (xSemaphoreTake(xSemaphore, TIMEOUT) == pdTRUE) { |
|||
int channel = 1; |
|||
int pin = SERVO_PINS[num]; |
|||
if (channel >= LEDC_NUM_CHANNELS) { |
|||
return; |
|||
} |
|||
|
|||
ledcSetup(channel, 50, TIMER_WIDTH); |
|||
ledcAttachPin(pin, channel); |
|||
ledcWrite(channel, MIN_PULSE_WIDTH); |
|||
delay(1000); |
|||
ledcWrite(channel, MAX_PULSE_WIDTH); |
|||
delay(1000); |
|||
xSemaphoreGive(xSemaphore); |
|||
} else { |
|||
return; |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
#ifndef _PUSHER_H |
|||
#define _PUSHER_H |
|||
|
|||
#include <cstdint> |
|||
#include "semaphore.h" |
|||
|
|||
static constexpr uint16_t MIN_PULSE_WIDTH = 544; // the shortest pulse sent to a servo
|
|||
static constexpr uint16_t MAX_PULSE_WIDTH = 2400; // the longest pulse sent to a servo
|
|||
static constexpr uint16_t DEFAULT_PULSE_WIDTH = 1500; // default pulse width
|
|||
|
|||
static constexpr uint8_t LEDC_NUM_CHANNELS = 8; // default pulse width
|
|||
|
|||
static constexpr TickType_t TIMEOUT = portMAX_DELAY; |
|||
|
|||
#define TIMER_WIDTH 16 |
|||
|
|||
class Pusher { |
|||
public: |
|||
Pusher(void); |
|||
void setup(void); |
|||
void dispense(int); |
|||
private: |
|||
volatile SemaphoreHandle_t xSemaphore; |
|||
}; |
|||
|
|||
#endif /* _PUSHER_H */ |
@ -0,0 +1,142 @@ |
|||
#include <WiFi.h> |
|||
#include <ESPmDNS.h> |
|||
#include <AsyncTCP.h> |
|||
#include <ESPAsyncWebServer.h> |
|||
|
|||
#include "webserver.h" |
|||
|
|||
using namespace std; |
|||
|
|||
Webserver::Webserver(void) |
|||
: server(80) { |
|||
} |
|||
|
|||
String processor(const String& var) |
|||
{ |
|||
if(var == "HELLO_FROM_TEMPLATE") |
|||
return F("Hello world!"); |
|||
if(var == "FACH0_NAME") |
|||
return F("Hello world!"); |
|||
if(var == "FACH1_NAME") |
|||
return F("Hello world!"); |
|||
if(var == "FACH2_NAME") |
|||
return F("Hello world!"); |
|||
return String(); |
|||
} |
|||
|
|||
const char index_html[] PROGMEM = |
|||
"<html><head><title>Vending Machine</title></head>" |
|||
"<body><h1>Vending Machine</h1>" |
|||
"<table><tr><td>%FACH0_NAME%</td><td>%FACH1_NAME%</td><td>%FACH2_NAME%</td></tr>" |
|||
"<tr><td></td><td></td><td></td></tr></table>" |
|||
"</body></html>"; // large char array, tested with 14k
|
|||
|
|||
void Webserver::start(void) { |
|||
startMDNS(); |
|||
|
|||
// respond to GET requests on URL /heap
|
|||
server.on("/heap", HTTP_GET, [](AsyncWebServerRequest *request){ |
|||
request->send(200, "text/plain", String(ESP.getFreeHeap())); |
|||
}); |
|||
|
|||
//First request will return 0 results unless you start scan from somewhere else (loop/setup)
|
|||
//Do not request more often than 3-5 seconds
|
|||
server.on("/scan", HTTP_GET, [](AsyncWebServerRequest *request){ |
|||
String json = "["; |
|||
int n = WiFi.scanComplete(); |
|||
if(n == -2){ |
|||
WiFi.scanNetworks(true); |
|||
} else if(n){ |
|||
for (int i = 0; i < n; ++i){ |
|||
if(i) json += ","; |
|||
json += "{"; |
|||
json += "\"rssi\":"+String(WiFi.RSSI(i)); |
|||
json += ",\"ssid\":\""+WiFi.SSID(i)+"\""; |
|||
json += ",\"bssid\":\""+WiFi.BSSIDstr(i)+"\""; |
|||
json += ",\"channel\":"+String(WiFi.channel(i)); |
|||
json += ",\"secure\":"+String(WiFi.encryptionType(i)); |
|||
// json += ",\"hidden\":"+String(WiFi.isHidden(i)?"true":"false");
|
|||
json += "}"; |
|||
} |
|||
WiFi.scanDelete(); |
|||
if(WiFi.scanComplete() == -2){ |
|||
WiFi.scanNetworks(true); |
|||
} |
|||
} |
|||
json += "]"; |
|||
request->send(200, "text/json", json); |
|||
json = String(); |
|||
}); |
|||
|
|||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ |
|||
request->send_P(200, "text/html", index_html, processor); |
|||
}); |
|||
|
|||
server.onNotFound([](AsyncWebServerRequest *request){ |
|||
Serial.printf("NOT_FOUND: "); |
|||
if(request->method() == HTTP_GET) |
|||
Serial.printf("GET"); |
|||
else if(request->method() == HTTP_POST) |
|||
Serial.printf("POST"); |
|||
else if(request->method() == HTTP_DELETE) |
|||
Serial.printf("DELETE"); |
|||
else if(request->method() == HTTP_PUT) |
|||
Serial.printf("PUT"); |
|||
else if(request->method() == HTTP_PATCH) |
|||
Serial.printf("PATCH"); |
|||
else if(request->method() == HTTP_HEAD) |
|||
Serial.printf("HEAD"); |
|||
else if(request->method() == HTTP_OPTIONS) |
|||
Serial.printf("OPTIONS"); |
|||
else |
|||
Serial.printf("UNKNOWN"); |
|||
Serial.printf(" http://%s%s\n", request->host().c_str(), request->url().c_str()); |
|||
|
|||
if(request->contentLength()){ |
|||
Serial.printf("_CONTENT_TYPE: %s\n", request->contentType().c_str()); |
|||
Serial.printf("_CONTENT_LENGTH: %u\n", request->contentLength()); |
|||
} |
|||
|
|||
int headers = request->headers(); |
|||
int i; |
|||
for(i=0;i<headers;i++){ |
|||
AsyncWebHeader* h = request->getHeader(i); |
|||
Serial.printf("_HEADER[%s]: %s\n", h->name().c_str(), h->value().c_str()); |
|||
} |
|||
|
|||
int params = request->params(); |
|||
for(i=0;i<params;i++){ |
|||
AsyncWebParameter* p = request->getParam(i); |
|||
if(p->isFile()){ |
|||
Serial.printf("_FILE[%s]: %s, size: %u\n", p->name().c_str(), p->value().c_str(), p->size()); |
|||
} else if(p->isPost()){ |
|||
Serial.printf("_POST[%s]: %s\n", p->name().c_str(), p->value().c_str()); |
|||
} else { |
|||
Serial.printf("_GET[%s]: %s\n", p->name().c_str(), p->value().c_str()); |
|||
} |
|||
} |
|||
|
|||
request->send(404); |
|||
}); |
|||
|
|||
server.begin(); |
|||
Serial.println("Webserver started."); |
|||
} |
|||
|
|||
void Webserver::startMDNS(void) { |
|||
// Set up mDNS responder:
|
|||
// - first argument is the domain name, in this example
|
|||
// the fully-qualified domain name is "esp8266.local"
|
|||
// - second argument is the IP address to advertise
|
|||
// we send our IP address on the WiFi network
|
|||
if (!MDNS.begin("esp32")) { |
|||
Serial.println("Error setting up MDNS responder!"); |
|||
while(1) { |
|||
delay(1000); |
|||
} |
|||
} |
|||
Serial.println("mDNS responder started"); |
|||
|
|||
// Add service to MDNS-SD
|
|||
MDNS.addService("http", "tcp", 80); |
|||
} |
@ -0,0 +1,16 @@ |
|||
#ifndef _WEBSERVER_H |
|||
#define _WEBSERVER_H |
|||
|
|||
#include <AsyncTCP.h> |
|||
#include <ESPAsyncWebServer.h> |
|||
|
|||
class Webserver { |
|||
public: |
|||
Webserver(void); |
|||
void start(void); |
|||
private: |
|||
void startMDNS(void); |
|||
AsyncWebServer server; |
|||
}; |
|||
|
|||
#endif /* _WEBSERVER_H */ |
@ -0,0 +1,65 @@ |
|||
#include <WiFi.h> |
|||
#include <WiFiMulti.h> |
|||
#include <Preferences.h> |
|||
|
|||
#include "hardware.h" |
|||
#include "wifi.h" |
|||
|
|||
using namespace std; |
|||
|
|||
Wifi::Wifi() {} |
|||
|
|||
void Wifi::reconnectTask(void* parameters) { |
|||
while(true) { |
|||
if(wifiMulti.run() != WL_CONNECTED) { |
|||
Serial.println("WiFi not connected!"); |
|||
} |
|||
vTaskDelay(3000 / portTICK_PERIOD_MS); |
|||
} |
|||
Serial.println("WiFi disconnecting!"); |
|||
WiFi.disconnect(); |
|||
vTaskDelete(NULL); |
|||
} |
|||
|
|||
void Wifi::cTaskWrapper(void* parameters) { |
|||
static_cast<Wifi*>(parameters)->reconnectTask(NULL); |
|||
} |
|||
|
|||
void Wifi::connect() { |
|||
wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD); |
|||
|
|||
Serial.println("Connecting to WiFi network"); |
|||
if(wifiMulti.run() == WL_CONNECTED) { |
|||
Serial.println(""); |
|||
Serial.println("WiFi connected"); |
|||
Serial.println("IP address: "); |
|||
Serial.println(WiFi.localIP()); |
|||
} |
|||
|
|||
xTaskCreate( |
|||
&cTaskWrapper, /* Task function. */ |
|||
"wifiReconnectTask", /* String with name of task. */ |
|||
1024, /* Stack size in words. */ |
|||
this, /* Parameter passed as input of the task */ |
|||
1, /* Priority of the task. */ |
|||
&wifiTaskHandle); /* Task handle. */ |
|||
} |
|||
|
|||
void Wifi::disconnect() { |
|||
} |
|||
|
|||
size_t Wifi::getStoredPassword(const char* ssid, char* value) { |
|||
Preferences preferences; |
|||
preferences.begin("wifi-pwd", false); |
|||
size_t len = preferences.getString(ssid, value, 64); |
|||
preferences.end(); |
|||
return len; |
|||
} |
|||
|
|||
size_t Wifi::setStoredPassword(const char* ssid, char* value) { |
|||
Preferences preferences; |
|||
preferences.begin("wifi-pwd", false); |
|||
size_t len = preferences.putString(ssid, value); |
|||
preferences.end(); |
|||
return len; |
|||
} |
@ -0,0 +1,21 @@ |
|||
#ifndef _WIFI_H |
|||
#define _WIFI_H |
|||
|
|||
#include <WiFi.h> |
|||
#include <WiFiMulti.h> |
|||
|
|||
class Wifi { |
|||
public: |
|||
Wifi(); |
|||
void connect(); |
|||
void disconnect(); |
|||
private: |
|||
void reconnectTask(void*); |
|||
static void cTaskWrapper(void*); |
|||
WiFiMulti wifiMulti; |
|||
TaskHandle_t wifiTaskHandle; |
|||
size_t getStoredPassword(const char* ssid, char* value); |
|||
size_t setStoredPassword(const char* ssid, char* value); |
|||
}; |
|||
|
|||
#endif /* _WIFI_H */ |
Loading…
Reference in new issue