Browse Source

add vending code

main
Hendrik Langer 6 years ago
parent
commit
16d9518cdc
  1. 2
      software/src/hardware.h
  2. 27
      software/src/item.cpp
  3. 15
      software/src/item.h
  4. 14
      software/src/main.cpp
  5. 6
      software/src/pusher.cpp
  6. 16
      software/src/pusher.h
  7. 29
      software/src/shelf.cpp
  8. 17
      software/src/shelf.h
  9. 97
      software/src/webserver.cpp
  10. 3
      software/src/wifi.cpp
  11. 2
      software/src/wifi.h

2
software/src/hardware.h

@ -6,6 +6,6 @@
#define LED_BUILTIN 13
static constexpr int SERVO_PINS[] = {22, 23, 24, 25};
static constexpr int SERVO_PINS[] = {5, 18, 24, 25};
#endif /* _HARDWARE_H */

27
software/src/item.cpp

@ -0,0 +1,27 @@
#include <Preferences.h>
#include <nvs_flash.h>
#include "item.h"
using namespace std;
extern volatile SemaphoreHandle_t xPreferencesSemaphore;
Item::Item(int num) {
if (xSemaphoreTake(xPreferencesSemaphore, portMAX_DELAY) == pdTRUE) {
Preferences preferences;
preferences.begin("vending-items", false);
size_t len;
char buffer[50];
sprintf(buffer, "item%u_name", num);
preferences.getString(buffer, name, sizeof(name));
sprintf(buffer, "item%u_quantity", num);
quantity = preferences.getUInt(buffer, 0);
sprintf(buffer, "item%u_price", num);
price = preferences.getUInt(buffer, 0);
preferences.end();
xSemaphoreGive(xPreferencesSemaphore);
} else {
Serial.print("preferences not available");
}
}

15
software/src/item.h

@ -0,0 +1,15 @@
#ifndef _ITEM_H
#define _ITEM_H
#include <Preferences.h>
class Item {
public:
Item(int);
char name[16];
unsigned int quantity;
unsigned int price;
private:
};
#endif /* _ITEM_H */

14
software/src/main.cpp

@ -5,27 +5,39 @@
*/
#include <Arduino.h>
#include <nvs_flash.h>
#include "hardware.h"
#include "wifi.h"
#include "webserver.h"
#include "pusher.h"
#include "shelf.h"
volatile SemaphoreHandle_t xPreferencesSemaphore = xSemaphoreCreateMutex();
Wifi wifi;
Webserver webserver;
Pusher pusher;
Shelf* shelf;
void setup()
{
Serial.begin(112500);
esp_err_t err = nvs_flash_init();
/* if (err != ESP_OK){
Serial.print("NVS readStruct error @ nvs_init:");
Serial.println(err,HEX);
}*/
Serial.println("Hello");
// initialize LED digital pin as an output.
pinMode(LED_BUILTIN, OUTPUT);
wifi.connect();
webserver.start();
shelf = new Shelf(8);
pusher.setup();
webserver.start();
}
void loop()

6
software/src/pusher.cpp

@ -1,6 +1,7 @@
#include <Arduino.h>
#include "esp32-hal-ledc.h"
#include "semaphore.h"
#include "driver/ledc.h"
#include "hardware.h"
#include "pusher.h"
@ -12,7 +13,7 @@ Pusher::Pusher(void) {
}
void Pusher::setup(void) {
int channel = 1;
int channel = LEDC_TIMER_0;
int pin = 22;
for(auto pin : SERVO_PINS) {
if (xSemaphoreTake(xSemaphore, TIMEOUT) == pdTRUE) {
@ -25,7 +26,7 @@ void Pusher::setup(void) {
}
}
void Pusher::dispense(int num) {
void Pusher::push(int num) {
if (xSemaphoreTake(xSemaphore, TIMEOUT) == pdTRUE) {
int channel = 1;
int pin = SERVO_PINS[num];
@ -39,6 +40,7 @@ void Pusher::dispense(int num) {
delay(1000);
ledcWrite(channel, MAX_PULSE_WIDTH);
delay(1000);
ledcDetachPin(pin);
xSemaphoreGive(xSemaphore);
} else {
return;

16
software/src/pusher.h

@ -2,23 +2,27 @@
#define _PUSHER_H
#include <cstdint>
#include "driver/ledc.h"
#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 uint16_t MIN_PULSE_MS = 544; // the shortest pulse sent to a servo
static constexpr uint16_t MAX_PULSE_MS = 2400; // the longest pulse sent to a servo
static constexpr uint16_t DEFAULT_PULSE_MS = 1500; // default pulse width
static constexpr int TIMER_WIDTH = LEDC_TIMER_15_BIT;
static constexpr uint16_t MIN_PULSE_WIDTH = (1 << TIMER_WIDTH) * MIN_PULSE_MS / 20000; // the shortest pulse sent to a servo
static constexpr uint16_t MAX_PULSE_WIDTH = (1 << TIMER_WIDTH) * MAX_PULSE_MS / 20000; // the longest pulse sent to a servo
static constexpr uint16_t DEFAULT_PULSE_WIDTH = (1 << TIMER_WIDTH) * DEFAULT_PULSE_MS / 20000; // 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);
void push(int);
private:
volatile SemaphoreHandle_t xSemaphore;
};

29
software/src/shelf.cpp

@ -0,0 +1,29 @@
#include "shelf.h"
#include "item.h"
#include "pusher.h"
using namespace std;
extern Pusher pusher;
Shelf::Shelf(int size)
: num_items(size) {
for(int i=0; i<size; i++) {
item.push_back(new Item(i));
}
}
void Shelf::reload(void) {
for(int i=0; i<num_items; i++) {
delete item.at(i);
}
item.clear();
for(int i=0; i<num_items; i++) {
item.push_back(new Item(i));
}
}
void Shelf::dispense(int num) {
item.at(num)->quantity--;
pusher.push(num);
}

17
software/src/shelf.h

@ -0,0 +1,17 @@
#ifndef _SHELF_H
#define _SHELF_H
#include <vector>
#include "item.h"
class Shelf {
public:
Shelf(int);
int num_items;;
std::vector<Item*> item;
void reload(void);
void dispense(int);
private:
};
#endif /* _SHELF_H */

97
software/src/webserver.cpp

@ -2,33 +2,56 @@
#include <ESPmDNS.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Preferences.h>
#include "shelf.h"
#include "webserver.h"
using namespace std;
extern Shelf* shelf;
extern volatile SemaphoreHandle_t xPreferencesSemaphore;
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!");
if(var == "HELLO_FROM_TEMPLATE")
return F("Hello world!");
if (var.startsWith("ITEM")) {
int num = var.substring(4,5).toInt();
if (var.endsWith("_NAME")) {
return shelf->item.at(num)->name;
} else if (var.endsWith("_QUANTITY")) {
return String(shelf->item.at(num)->quantity);
} else if (var.endsWith("_PRICE")) {
return String(shelf->item.at(num)->price);
}
}
return String();
}
void requestDispense(AsyncWebServerRequest *request) {
if(request->hasParam("num")) {
AsyncWebParameter* p = request->getParam("num");
const int num = atoi( p->value().c_str() );
shelf->dispense(num);
}
request->send(200, "text/plain", "OK");
}
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>"
"<html><head><title>Vending Machine</title><style>table, th, td { border: 2px solid black; }</style></head>"
"<body><h1>Vending Machine</h1><table>"
"<tr><td><strong>%ITEM0_NAME%</strong><br/><span style='text-align:left; float:left;'>%ITEM0_QUANTITY%</span><span style='text-align:right; float:right;'>%ITEM0_PRICE%</span></td><td><strong>%ITEM1_NAME%</strong><br/><span style='text-align:left; float:left;'>%ITEM1_QUANTITY%</span><span style='text-align:right; float:right;'>%ITEM1_PRICE%</span></td><td><strong>%ITEM2_NAME%</strong><br/><span style='text-align:left; float:left;'>%ITEM2_QUANTITY%</span><span style='text-align:right; float:right;'>%ITEM2_PRICE%</span></td></tr>"
"<tr><td><a href=\"/dispense?num=0\">DISPENSE</a></td><td><a href=\"/dispense?num=1\">DISPENSE</a></td><td><a href=\"/dispense?num=2\">DISPENSE</a></td></tr>"
"<tr><td><strong>%ITEM3_NAME%</strong><br/><span style='text-align:left; float:left;'>%ITEM3_QUANTITY%</span><span style='text-align:right; float:right;'>%ITEM3_PRICE%</span></td><td><strong>%ITEM4_NAME%</strong><br/><span style='text-align:left; float:left;'>%ITEM4_QUANTITY%</span><span style='text-align:right; float:right;'>%ITEM4_PRICE%</span></td><td><strong>%ITEM5_NAME%</strong><br/><span style='text-align:left; float:left;'>%ITEM5_QUANTITY%</span><span style='text-align:right; float:right;'>%ITEM5_PRICE%</span></td></tr>"
"<tr><td><a href=\"/dispense?num=3\">DISPENSE</a></td><td><a href=\"/dispense?num=4\">DISPENSE</a></td><td><a href=\"/dispense?num=5\">DISPENSE</a></td></tr>"
"</table><hr /><a href=\"/configure\">configure</a>"
"</body></html>"; // large char array, tested with 14k
void Webserver::start(void) {
@ -72,6 +95,56 @@ void Webserver::start(void) {
request->send_P(200, "text/html", index_html, processor);
});
server.on("/dispense", HTTP_GET, requestDispense);
// Config
server.on("/configure", HTTP_GET, [](AsyncWebServerRequest *request){
AsyncResponseStream *response = request->beginResponseStream("text/html");
response->printf("<!DOCTYPE html><html><head><title>Vending Machine at %s</title></head><body>", request->url().c_str());
response->print("<form method='POST' action='/configure' enctype='multipart/form-data'>");
for(int i=0; i<shelf->num_items; i++) {
response->printf("Item %u: ", i);
response->printf("<input type='text' name='item%u_name' value='%s'>", i, shelf->item.at(i)->name);
response->printf("<input type='text' name='item%u_quantity' value='%u'>", i, shelf->item.at(i)->quantity);
response->printf("<input type='text' name='item%u_price' value='%u'>", i, shelf->item.at(i)->price);
response->print("<hr />");
}
response->print("<input type='submit' value='Update'></form>");
response->print("</body></html>");
request->send(response);
});
server.on("/configure", HTTP_POST, [](AsyncWebServerRequest *request){
if (xSemaphoreTake(xPreferencesSemaphore, portMAX_DELAY) == pdTRUE) {
Preferences preferences;
preferences.begin("vending-items", false);
for (int i=0; i<shelf->num_items; i++) {
char buffer[50];
sprintf(buffer, "item%u_name", i);
if(request->hasParam(buffer, true)) {
AsyncWebParameter* p = request->getParam(buffer, true);
const char* name = p->value().c_str();
preferences.putString(buffer, name);
}
sprintf(buffer, "item%u_quantity", i);
if(request->hasParam(buffer, true)) {
AsyncWebParameter* p = request->getParam(buffer, true);
const int num = atoi( p->value().c_str() );
preferences.putUInt(buffer, num);
}
sprintf(buffer, "item%u_price", i);
if(request->hasParam(buffer, true)) {
AsyncWebParameter* p = request->getParam(buffer, true);
const int price = atoi( p->value().c_str() );
preferences.putUInt(buffer, price);
}
}
preferences.end();
xSemaphoreGive(xPreferencesSemaphore);
shelf->reload();
}
request->send(200, "text/html", "OK");
});
server.onNotFound([](AsyncWebServerRequest *request){
Serial.printf("NOT_FOUND: ");
if(request->method() == HTTP_GET)

3
software/src/wifi.cpp

@ -47,7 +47,7 @@ xTaskCreate(
void Wifi::disconnect() {
}
/*
size_t Wifi::getStoredPassword(const char* ssid, char* value) {
Preferences preferences;
preferences.begin("wifi-pwd", false);
@ -63,3 +63,4 @@ size_t Wifi::setStoredPassword(const char* ssid, char* value) {
preferences.end();
return len;
}
*/

2
software/src/wifi.h

@ -14,8 +14,6 @@ class Wifi {
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…
Cancel
Save