2023-11-28 21:48:48 +00:00
|
|
|
#include <Arduino_MKRIoTCarrier.h>
|
|
|
|
#include <WiFiNINA.h>
|
|
|
|
#include <ArduinoMqttClient.h>
|
2023-12-20 20:07:09 +00:00
|
|
|
#include "motor.h"
|
2023-11-28 21:48:48 +00:00
|
|
|
#include "secrets.h"
|
|
|
|
|
2023-12-20 21:42:42 +00:00
|
|
|
#define INPUT_SIZE 4
|
|
|
|
|
|
|
|
enum state {
|
|
|
|
STATE_CONNECTING,
|
|
|
|
STATE_LOGGED_OUT,
|
|
|
|
STATE_LOGGED_IN,
|
|
|
|
STATE_INPUT_PASSCODE,
|
|
|
|
};
|
|
|
|
|
|
|
|
enum state current_state;
|
|
|
|
char input_code[INPUT_SIZE + 1];
|
2023-12-20 22:19:36 +00:00
|
|
|
int user_id;
|
2023-12-20 21:42:42 +00:00
|
|
|
|
2023-11-28 21:48:48 +00:00
|
|
|
MKRIoTCarrier carrier;
|
|
|
|
WiFiSSLClient wifi_client;
|
|
|
|
MqttClient mqtt_client(wifi_client);
|
|
|
|
|
2023-12-20 20:32:14 +00:00
|
|
|
// Motor control variables
|
2023-11-29 15:00:54 +00:00
|
|
|
int motor_count = 0;
|
2023-12-20 15:43:56 +00:00
|
|
|
int motor_count_max = 64;
|
2023-12-20 16:29:11 +00:00
|
|
|
|
2023-12-20 22:19:36 +00:00
|
|
|
uint32_t red_led_color = carrier.leds.Color(255, 0, 0);
|
2023-12-20 20:07:09 +00:00
|
|
|
uint32_t green_led_color = carrier.leds.Color(0, 255, 0);
|
2023-12-20 22:19:36 +00:00
|
|
|
uint32_t blue_led_color = carrier.leds.Color(0, 0, 255);
|
2023-11-28 21:48:48 +00:00
|
|
|
|
2023-12-20 21:42:42 +00:00
|
|
|
bool is_input_empty()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < INPUT_SIZE; i++) {
|
|
|
|
if (input_code[i]) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_input_full()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < INPUT_SIZE; i++) {
|
|
|
|
if (!input_code[i]) return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void change_state(enum state new_state)
|
|
|
|
{
|
|
|
|
// Initialize state
|
|
|
|
switch (new_state) {
|
|
|
|
case STATE_INPUT_PASSCODE:
|
|
|
|
input_code[0] = '\0';
|
|
|
|
input_code[1] = '\0';
|
|
|
|
input_code[2] = '\0';
|
|
|
|
input_code[3] = '\0';
|
|
|
|
input_code[4] = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_state = new_state;
|
|
|
|
draw_state();
|
|
|
|
}
|
|
|
|
|
|
|
|
void draw_state()
|
|
|
|
{
|
2023-12-20 22:19:36 +00:00
|
|
|
// Reset
|
2023-12-20 21:42:42 +00:00
|
|
|
carrier.display.fillScreen(0x000);
|
2023-12-20 22:19:36 +00:00
|
|
|
carrier.leds.fill(0, 0, 5);
|
|
|
|
carrier.leds.setBrightness(5);
|
2023-12-20 21:42:42 +00:00
|
|
|
|
|
|
|
switch (current_state) {
|
|
|
|
case STATE_CONNECTING:
|
|
|
|
carrier.display.setCursor(20, 100);
|
|
|
|
carrier.display.setTextSize(3);
|
|
|
|
carrier.display.print("Connecting..");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_LOGGED_OUT:
|
|
|
|
carrier.display.setCursor(15, 100);
|
|
|
|
carrier.display.setTextSize(2);
|
|
|
|
carrier.display.print("Press Green Button");
|
|
|
|
carrier.leds.fill(green_led_color, 2, 1);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_INPUT_PASSCODE:
|
|
|
|
if (is_input_empty()) {
|
|
|
|
carrier.display.setCursor(50, 100);
|
|
|
|
carrier.display.setTextSize(2);
|
|
|
|
carrier.display.print("Please enter");
|
|
|
|
carrier.display.setCursor(80, 120);
|
|
|
|
carrier.display.print("passcode");
|
|
|
|
} else {
|
|
|
|
carrier.display.setCursor(50, 100);
|
|
|
|
carrier.display.setTextSize(3);
|
|
|
|
for (int i = 0; i < INPUT_SIZE; i++) {
|
|
|
|
char ch = input_code[i];
|
|
|
|
if (!ch) ch = '_';
|
|
|
|
|
|
|
|
carrier.display.print(ch);
|
|
|
|
carrier.display.print(' ');
|
|
|
|
}
|
|
|
|
}
|
2023-12-20 22:19:36 +00:00
|
|
|
|
|
|
|
carrier.leds.fill(blue_led_color, 0, 2);
|
|
|
|
carrier.leds.fill(red_led_color, 2, 1);
|
|
|
|
carrier.leds.fill(blue_led_color, 3, 2);
|
2023-12-20 21:42:42 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_LOGGED_IN:
|
|
|
|
carrier.display.setCursor(40, 100);
|
|
|
|
carrier.display.setTextSize(3);
|
|
|
|
carrier.display.print("Logged in");
|
2023-12-20 22:19:36 +00:00
|
|
|
carrier.leds.fill(green_led_color, 2, 1);
|
|
|
|
carrier.leds.fill(red_led_color, 4, 1);
|
2023-12-20 21:42:42 +00:00
|
|
|
break;
|
|
|
|
}
|
2023-12-20 22:19:36 +00:00
|
|
|
|
|
|
|
carrier.leds.show();
|
2023-12-20 21:42:42 +00:00
|
|
|
}
|
|
|
|
|
2023-12-20 20:07:09 +00:00
|
|
|
void setup()
|
2023-11-28 21:48:48 +00:00
|
|
|
{
|
|
|
|
Serial.begin(9600);
|
|
|
|
|
|
|
|
Serial.println("Started");
|
|
|
|
|
2023-12-20 20:07:09 +00:00
|
|
|
carrier.noCase();
|
2023-11-28 21:48:48 +00:00
|
|
|
carrier.begin();
|
2023-12-20 20:07:09 +00:00
|
|
|
|
2023-11-28 21:48:48 +00:00
|
|
|
carrier.display.init(240, 240);
|
|
|
|
|
2023-12-20 20:07:09 +00:00
|
|
|
motor_setup();
|
|
|
|
|
2023-11-28 21:48:48 +00:00
|
|
|
pinMode(TFT_BACKLIGHT, OUTPUT);
|
|
|
|
digitalWrite(TFT_BACKLIGHT, HIGH);
|
|
|
|
|
2023-12-20 20:07:09 +00:00
|
|
|
change_state(STATE_CONNECTING);
|
2023-11-28 21:48:48 +00:00
|
|
|
|
|
|
|
Serial.println("Connecting to WiFi...");
|
|
|
|
|
|
|
|
if (wifi_enterprise) WiFi.beginEnterprise(wifi_ssid, wifi_username, wifi_password);
|
|
|
|
else WiFi.begin(wifi_ssid, wifi_password);
|
|
|
|
|
|
|
|
while (WiFi.status() != WL_CONNECTED) delay(1000);
|
|
|
|
|
|
|
|
Serial.println("Connected to WiFi, connecting to MQTT...");
|
|
|
|
|
|
|
|
mqtt_client.setUsernamePassword(mqtt_username, mqtt_password);
|
|
|
|
if (!mqtt_client.connect(mqtt_server, 8883)) {
|
|
|
|
Serial.print("MQTT connection failed with error code: ");
|
|
|
|
Serial.println(mqtt_client.connectError());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Serial.println("Connected to MQTT");
|
|
|
|
|
2023-12-20 20:07:09 +00:00
|
|
|
carrier.Buzzer.beep();
|
|
|
|
|
2023-11-28 21:48:48 +00:00
|
|
|
mqtt_client.subscribe("dispense");
|
2023-12-20 21:42:42 +00:00
|
|
|
mqtt_client.subscribe("login");
|
2023-11-28 21:48:48 +00:00
|
|
|
|
2023-12-20 20:07:09 +00:00
|
|
|
change_state(STATE_LOGGED_OUT);
|
2023-11-26 22:16:48 +00:00
|
|
|
}
|
|
|
|
|
2023-12-20 20:07:09 +00:00
|
|
|
void loop()
|
2023-11-28 21:48:48 +00:00
|
|
|
{
|
2023-12-20 20:07:09 +00:00
|
|
|
carrier.Buttons.update();
|
|
|
|
|
2023-11-29 15:25:57 +00:00
|
|
|
// Rotate motor if needed
|
|
|
|
if (motor_count) {
|
|
|
|
motor_clockwise();
|
|
|
|
motor_count--;
|
|
|
|
}
|
2023-11-29 15:00:54 +00:00
|
|
|
|
2023-11-29 15:25:57 +00:00
|
|
|
// Receive MQTT messages
|
2023-11-28 21:48:48 +00:00
|
|
|
int message_size = mqtt_client.parseMessage();
|
|
|
|
if (message_size) {
|
2023-12-20 21:42:42 +00:00
|
|
|
String topic = mqtt_client.messageTopic();
|
2023-11-28 21:48:48 +00:00
|
|
|
Serial.print("Got message with topic: ");
|
2023-12-20 21:42:42 +00:00
|
|
|
Serial.println(topic);
|
|
|
|
|
|
|
|
// Parse message
|
|
|
|
char *message = (char *)malloc(message_size + 1);
|
|
|
|
for (int i = 0; i < message_size && mqtt_client.available(); i++) {
|
|
|
|
message[i] = (char)mqtt_client.read();
|
|
|
|
}
|
|
|
|
message[message_size] = '\0';
|
2023-11-28 21:48:48 +00:00
|
|
|
|
2023-12-20 21:42:42 +00:00
|
|
|
Serial.print("and payload: ");
|
|
|
|
Serial.println(message);
|
|
|
|
|
|
|
|
// Check topic
|
|
|
|
if (topic == "dispense") {
|
2023-11-28 21:48:48 +00:00
|
|
|
Serial.println("Dispensing...");
|
|
|
|
|
2023-11-29 15:00:54 +00:00
|
|
|
// Start rotating motor
|
2023-12-20 22:19:36 +00:00
|
|
|
motor_count += motor_count_max;
|
2023-12-20 21:42:42 +00:00
|
|
|
} else if (topic == "login") {
|
|
|
|
Serial.println("Logged in");
|
|
|
|
|
|
|
|
if (sscanf(message, "%d", &user_id) > 0) {
|
|
|
|
change_state(STATE_LOGGED_IN);
|
|
|
|
} else {
|
|
|
|
change_state(STATE_INPUT_PASSCODE);
|
|
|
|
}
|
2023-11-28 21:48:48 +00:00
|
|
|
}
|
2023-12-20 21:42:42 +00:00
|
|
|
|
|
|
|
free(message);
|
2023-11-28 21:48:48 +00:00
|
|
|
}
|
2023-12-20 16:29:11 +00:00
|
|
|
|
2023-12-20 22:19:36 +00:00
|
|
|
touchButtons buttons[] = { TOUCH0, TOUCH1, TOUCH3, TOUCH4 };
|
|
|
|
char input_characters[] = "1234";
|
|
|
|
|
2023-12-20 21:42:42 +00:00
|
|
|
switch (current_state) {
|
|
|
|
case STATE_LOGGED_OUT:
|
|
|
|
if (carrier.Buttons.onTouchDown(TOUCH2)) {
|
|
|
|
change_state(STATE_INPUT_PASSCODE);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_INPUT_PASSCODE:
|
2023-12-20 22:19:36 +00:00
|
|
|
if (carrier.Buttons.onTouchDown(TOUCH2)) {
|
|
|
|
if (is_input_empty()) {
|
|
|
|
change_state(STATE_LOGGED_OUT);
|
|
|
|
} else {
|
|
|
|
for (int i = INPUT_SIZE - 1; i >= 0; i--) {
|
|
|
|
if (input_code[i]) {
|
|
|
|
input_code[i] = '\0';
|
|
|
|
draw_state();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-12-20 21:42:42 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++) {
|
|
|
|
if (carrier.Buttons.onTouchDown(buttons[i])) {
|
|
|
|
for (int j = 0; j < INPUT_SIZE; j++) {
|
|
|
|
if (!input_code[j]) {
|
|
|
|
input_code[j] = input_characters[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_input_full()) {
|
|
|
|
mqtt_client.beginMessage("login_request");
|
|
|
|
mqtt_client.print(input_code);
|
|
|
|
mqtt_client.endMessage();
|
|
|
|
}
|
|
|
|
|
|
|
|
draw_state();
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-20 22:19:36 +00:00
|
|
|
break;
|
|
|
|
case STATE_LOGGED_IN:
|
|
|
|
if (carrier.Buttons.onTouchDown(TOUCH2)) {
|
|
|
|
char *user_id_str = (char *)malloc(4);
|
|
|
|
snprintf(user_id_str, 4, "%d", user_id);
|
|
|
|
|
|
|
|
mqtt_client.beginMessage("dispense_request");
|
|
|
|
mqtt_client.print(user_id_str);
|
|
|
|
mqtt_client.endMessage();
|
|
|
|
|
|
|
|
free(user_id_str);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (carrier.Buttons.onTouchDown(TOUCH4)) {
|
|
|
|
change_state(STATE_LOGGED_OUT);
|
|
|
|
}
|
2023-12-20 21:42:42 +00:00
|
|
|
break;
|
|
|
|
}
|
2023-12-20 22:19:36 +00:00
|
|
|
|
|
|
|
delay(2);
|
2023-11-29 15:00:54 +00:00
|
|
|
}
|