Beginnings of a slack example.

This commit is contained in:
Nathanial Hendler 2021-09-11 16:48:28 -07:00
parent 35d597c368
commit cf56af61e3
13 changed files with 1083 additions and 298 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*.swp
credentials.h

View File

@ -0,0 +1,348 @@
/*
Copyright (c) 2020 Brian Lough. All right reserved.
ArduinoSlack - An Arduino library to wrap the Slack API
MIT License
*/
#include "ArduinoSlack.h"
ArduinoSlack::ArduinoSlack(Client &client, const char *bearerToken)
{
this->client = &client;
this->_bearerToken = bearerToken;
}
int ArduinoSlack::makeGetRequest(const char *command, const char *body, const char *contentType)
{
client->flush();
client->setTimeout(SLACK_TIMEOUT);
if (!client->connect(SLACK_HOST, portNumber))
{
SLACK_SERIAL_LN(F("Connection failed"));
return false;
}
// give the esp a breather
yield();
// Send HTTP request
client->print(F("GET "));
client->print(command);
client->println(F(" HTTP/1.1"));
//Headers
client->print(F("Host: "));
client->println(SLACK_HOST);
client->println(F("Accept: application/json"));
client->print(F("Content-Type: "));
client->println(contentType);
client->print(F("Authorization: Bearer "));
client->println(_bearerToken);
client->println(F("Cache-Control: no-cache"));
client->print(F("Content-Length: "));
client->println(strlen(body));
client->println();
//send Data here?
client->print(body);
if (client->println() == 0)
{
SLACK_SERIAL_LN(F("Failed to send request"));
return false;
}
int statusCode = getHttpStatusCode();
skipHeaders();
return statusCode;
}
int ArduinoSlack::makePostRequest(const char *command, const char *body, const char *contentType)
{
client->flush();
client->setTimeout(SLACK_TIMEOUT);
if (!client->connect(SLACK_HOST, portNumber))
{
SLACK_SERIAL_LN(F("Connection failed"));
return false;
}
// give the esp a breather
yield();
// Send HTTP request
client->print(F("POST "));
client->print(command);
client->println(F(" HTTP/1.1"));
//Headers
client->print(F("Host: "));
client->println(SLACK_HOST);
client->println(F("Accept: application/json"));
client->print(F("Content-Type: "));
client->println(contentType);
client->print(F("Authorization: Bearer "));
client->println(_bearerToken);
client->println(F("Cache-Control: no-cache"));
client->print(F("Content-Length: "));
client->println(strlen(body));
client->println();
//send Data here?
client->print(body);
if (client->println() == 0)
{
SLACK_SERIAL_LN(F("Failed to send request"));
return false;
}
int statusCode = getHttpStatusCode();
skipHeaders();
return statusCode;
}
bool ArduinoSlack::setPresence(const char *presence)
{
char command[100];
sprintf(command, SLACK_USERS_SET_PRESENCE_ENDPOINT, presence);
SLACK_DEBUG_SERIAL_LN(command);
// Get from https://arduinojson.org/v6/assistant/
const size_t bufferSize = 1000;
bool okStatus = false;
if (makePostRequest(command, "", "text/plain") == 200)
{
// Allocate DynamicJsonDocument
DynamicJsonDocument doc(bufferSize);
// Parse JSON object
DeserializationError error = deserializeJson(doc, *client);
if (!error)
{
SLACK_DEBUG_SERIAL_LN(F("parsed Json Object: "));
#ifdef SLACK_ENABLE_DEBUG
serializeJson(doc, Serial);
#endif
okStatus = doc["ok"];
if (!okStatus)
{
if (doc.containsKey("error"))
{
const char *errorMsg = doc["error"];
SLACK_DEBUG_SERIAL(F("Got the following error: "));
SLACK_DEBUG_SERIAL_LN(errorMsg);
}
else
{
SLACK_DEBUG_SERIAL_LN(F("Unkown Error"));
}
}
}
else
{
SLACK_DEBUG_SERIAL(F("deserializeJson() failed with code "));
SLACK_DEBUG_SERIAL_LN(error.c_str());
}
}
closeClient();
return okStatus;
}
SlackConvoHist ArduinoSlack::conversationHistory(const char *channel, const char *limit)
{
SLACK_DEBUG_SERIAL_LN(F("--------------"));
SLACK_DEBUG_SERIAL_LN(F("conversationHistory()"));
char body[300];
sprintf(body, setConvoHistoryBody, channel, limit);
SLACK_DEBUG_SERIAL_LN(body);
// Get from https://arduinojson.org/v6/assistant/
const size_t bufferSize = profileBufferSize;
SlackConvoHist conversation;;
// This flag will get cleared if all goes well
conversation.error = true;
if (makePostRequest(SLACK_CONVERSATIONS_HISTORY_ENDPOINT, body) == 200)
{
// Allocate DynamicJsonDocument
DynamicJsonDocument doc(bufferSize);
// Parse JSON object
DeserializationError error = deserializeJson(doc, *client);
if (!error)
{
SLACK_DEBUG_SERIAL_LN(F("parsed Json Object: "));
#ifdef SLACK_ENABLE_DEBUG
serializeJson(doc, Serial);
#endif
JsonObject messageObj = doc["messages"];
conversation.messageObj = messageObj;
//message.text = (char *)messageObj["text"].as<char *>();
//message.username = (char *)messageObj["username"].as<char *>();
//message.bot_id = (char *)messageObj["bot_id"].as<char *>();
//message.type = (char *)messageObj["type"].as<char *>();
conversation.error = false;
}
else
{
SLACK_DEBUG_SERIAL(F("deserializeJson() failed with code "));
SLACK_DEBUG_SERIAL_LN(error.c_str());
}
}
closeClient();
return conversation;
}
SlackMessage ArduinoSlack::postMessage(const char *channel, const char *text)
{
char body[300];
sprintf(body, setMessageBody, channel, text);
SLACK_DEBUG_SERIAL_LN(body);
// Get from https://arduinojson.org/v6/assistant/
const size_t bufferSize = profileBufferSize;
SlackMessage message;
// This flag will get cleared if all goes well
message.error = true;
if (makePostRequest(SLACK_POST_MESSAGE_ENDPOINT, body) == 200)
{
// Allocate DynamicJsonDocument
DynamicJsonDocument doc(bufferSize);
// Parse JSON object
DeserializationError error = deserializeJson(doc, *client);
if (!error)
{
SLACK_DEBUG_SERIAL_LN(F("parsed Json Object: "));
#ifdef SLACK_ENABLE_DEBUG
serializeJson(doc, Serial);
#endif
JsonObject messageObj = doc["message"];
message.text = (char *)messageObj["text"].as<char *>();
message.username = (char *)messageObj["username"].as<char *>();
message.bot_id = (char *)messageObj["bot_id"].as<char *>();
message.type = (char *)messageObj["type"].as<char *>();
message.error = false;
}
else
{
SLACK_DEBUG_SERIAL(F("deserializeJson() failed with code "));
SLACK_DEBUG_SERIAL_LN(error.c_str());
}
}
closeClient();
return message;
}
SlackProfile ArduinoSlack::setCustomStatus(const char *text, const char *emoji, int expiration)
{
SLACK_DEBUG_SERIAL_LN(F("setCustomStatus()"));
char body[300];
sprintf(body, setEndpointBody, text, emoji, expiration);
SLACK_DEBUG_SERIAL_LN(body);
// Get from https://arduinojson.org/v6/assistant/
const size_t bufferSize = profileBufferSize;
SlackProfile profile;
// This flag will get cleared if all goes well
profile.error = true;
if (makePostRequest(SLACK_USERS_PROFILE_SET_ENDPOINT, body) == 200)
{
// Allocate DynamicJsonDocument
DynamicJsonDocument doc(bufferSize);
// Parse JSON object
DeserializationError error = deserializeJson(doc, *client);
if (!error)
{
SLACK_DEBUG_SERIAL_LN(F("parsed Json Object: "));
#ifdef SLACK_ENABLE_DEBUG
serializeJson(doc, Serial);
#endif
JsonObject profileObj = doc["profile"];
profile.displayName = (char *)profileObj["display_name"].as<char *>();
profile.statusText = (char *)profileObj["status_text"].as<char *>();
profile.statusEmoji = (char *)profileObj["status_emoji"].as<char *>();
profile.statusExpiration = profileObj["status_expiration"].as<int>();
profile.error = false;
}
else
{
SLACK_DEBUG_SERIAL(F("deserializeJson() failed with code "));
SLACK_DEBUG_SERIAL_LN(error.c_str());
}
}
closeClient();
return profile;
}
void ArduinoSlack::skipHeaders(bool tossUnexpectedForJSON)
{
// Skip HTTP headers
if (!client->find("\r\n\r\n"))
{
SLACK_SERIAL_LN(F("Invalid response"));
return;
}
if (tossUnexpectedForJSON)
{
// Was getting stray characters between the headers and the body
// This should toss them away
while (client->available() && client->peek() != '{')
{
char c = 0;
client->readBytes(&c, 1);
SLACK_DEBUG_SERIAL(F("Tossing an unexpected character: "));
SLACK_DEBUG_SERIAL_LN(c);
}
}
}
int ArduinoSlack::getHttpStatusCode()
{
// Check HTTP status
if (client->find("HTTP/1.1"))
{
int statusCode = client->parseInt();
SLACK_DEBUG_SERIAL(F("Status Code: "));
SLACK_DEBUG_SERIAL_LN(statusCode);
return statusCode;
}
return -1;
}
void ArduinoSlack::closeClient()
{
if (client->connected())
{
SLACK_DEBUG_SERIAL_LN(F("Closing client"));
client->stop();
}
}

View File

@ -0,0 +1,104 @@
/*
Copyright (c) 2020 Brian Lough. All right reserved.
ArduinoSlack - An Arduino library to wrap the Slack API
MIT License
*/
#ifndef ArduinoSlack_h
#define ArduinoSlack_h
#include <Arduino.h>
#include <ArduinoJson.h>
#include <Client.h>
#define SLACK_ENABLE_SERIAL
//un-mark following line to enable debug mode
#define SLACK_ENABLE_DEBUG
#ifdef SLACK_ENABLE_SERIAL
#define SLACK_SERIAL(STR) Serial.print(STR)
#define SLACK_SERIAL_LN(STR) Serial.println(STR)
#else
#define SLACK_SERIAL(STR)
#define SLACK_SERIAL_LN(STR)
#endif
#ifdef SLACK_ENABLE_DEBUG
#define SLACK_DEBUG_SERIAL(STR) Serial.print(STR)
#define SLACK_DEBUG_SERIAL_LN(STR) Serial.println(STR)
#else
#define SLACK_DEBUG_SERIAL(STR)
#define SLACK_DEBUG_SERIAL_LN(STR)
#endif
#define SLACK_HOST "slack.com"
// Fingerprint valid from Tue, 13 Apr 2021 00:00:00 GMT
#define SLACK_FINGERPRINT "C3 CC ED 77 87 19 6D E7 76 5E AA A7 3D 67 7E CA 95 D2 46 E2"
#define SLACK_TIMEOUT 2000
#define SLACK_PRESENCE_AWAY "away"
#define SLACK_PRESENCE_AUTO "auto"
#define SLACK_USERS_PROFILE_SET_ENDPOINT "/api/users.profile.set"
#define SLACK_USERS_SET_PRESENCE_ENDPOINT "/api/users.setPresence?presence=%s"
#define SLACK_POST_MESSAGE_ENDPOINT "/api/chat.postMessage"
#define SLACK_CONVERSATIONS_HISTORY_ENDPOINT "/api/conversations.history"
struct SlackProfile
{
char *displayName;
char *statusText;
char *statusEmoji;
int statusExpiration;
bool error;
};
struct SlackMessage
{
char *text;
char *username;
char *bot_id;
char *type;
char *ts;
bool error;
};
struct SlackConvoHist
{
JsonObject messageObj;
bool error;
};
class ArduinoSlack
{
public:
ArduinoSlack(Client &client, const char *bearerToken);
int makePostRequest(const char *command, const char *body, const char *contentType = "application/json");
SlackProfile setCustomStatus(const char *text, const char *emoji, int expiration = 0);
SlackMessage postMessage(const char *channel, const char *text);
SlackConvoHist conversationHistory(const char *channel, const char *limit);
bool setPresence(const char *presence);
int portNumber = 443;
int profileBufferSize = 10000;
Client *client;
private:
const char *_bearerToken;
int getHttpStatusCode();
void skipHeaders(bool tossUnexpectedForJSON = true);
void closeClient();
const char *setConvoHistoryBody =
R"({"channel": "%s", "limit": "%s"})";
const char *setMessageBody =
R"({"channel": "%s", "text": "%s"})";
const char *setEndpointBody =
R"({"profile": { "status_text": "%s","status_emoji": "%s","status_expiration": %d}})";
};
#endif

View File

@ -0,0 +1,25 @@
// Digi Cert Global Root Cert - as of 26/08/2020
// Useful for ESP32
// Usage: client.setCACert(slack_server_cert);
const char *slack_server_cert = "-----BEGIN CERTIFICATE-----\n"
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n"
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n"
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n"
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n"
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n"
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n"
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n"
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n"
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n"
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n"
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n"
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n"
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n"
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n"
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n"
"-----END CERTIFICATE-----\n";

View File

@ -0,0 +1,151 @@
/*******************************************************************
Sets a custom status on your slack account. It will toggle between
two every 30 seconds
You will need a bearer token, see readme for more details
Parts:
ESP32 D1 Mini style Dev board* - http://s.click.aliexpress.com/e/C6ds4my
* * = Affiliate
If you find what I do useful and would like to support me,
please consider becoming a sponsor on Github
https://github.com/sponsors/witnessmenow/
Written by Brian Lough
YouTube: https://www.youtube.com/brianlough
Tindie: https://www.tindie.com/stores/brianlough/
Twitter: https://twitter.com/witnessmenow
*******************************************************************/
// ----------------------------
// Standard Libraries
// ----------------------------
#include <WiFi.h>
#include <WiFiClientSecure.h>
// ----------------------------
// Additional Libraries - each one of these will need to be installed.
// ----------------------------
#include "ArduinoSlack.h"
// Library for connecting to the Slack API
// Install from Github
// https://github.com/witnessmenow/arduino-slack-api
#include <ArduinoJson.h>
// Library used for parsing Json from the API responses
// Search for "Arduino Json" in the Arduino Library manager
// https://github.com/bblanchon/ArduinoJson
#include "credentials.h"
// copy credentials.h.sample to credentials.h and edit
// so it contains your passwords and tokens.
//------- Replace the following! ------
//char ssid[] = "SSID"; // your network SSID (name)
//char password[] = "password"; // your network password
char ssid[] = WIFI_SSID; // your network SSID (name)
char password[] = WIFI_PASSWORD; // your network password
//#define SLACK_ACCESS_TOKEN "AAAAAAAAAABBBBBBBBBBBCCCCCCCCCCCDDDDDDDDDDD"
//------- ---------------------- ------
// including a "slack_server_cert" variable
// header is included as part of the ArduinoSlack libary
#include "ArduinoSlackCert.h"
WiFiClientSecure client;
ArduinoSlack slack(client, SLACK_ACCESS_TOKEN);
unsigned long delayBetweenRequests = 120000; // Time between requests
unsigned long requestDueTime; //time when request due
bool firstStatus = true;
void setup()
{
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.println("");
Serial.println("Wifi credentials:");
Serial.println(ssid);
Serial.println(password);
Serial.print("Connecting to wifi.");
// Wait for connection
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
client.setCACert(slack_server_cert);
// If you want to enable some extra debugging
// uncomment the "#define SLACK_ENABLE_DEBUG" in ArduinoSlack.h
}
void displayProfile(SlackProfile profile)
{
if (!profile.error)
{
Serial.println("--------- Profile ---------");
Serial.print("Display Name: ");
Serial.println(profile.displayName);
Serial.print("Status Text: ");
Serial.println(profile.statusText);
Serial.print("Status Emoji: ");
Serial.println(profile.statusEmoji);
Serial.print("Status Expiration: ");
Serial.println(profile.statusExpiration);
Serial.println("------------------------");
}
else
{
Serial.println("error getting profile");
}
}
void loop()
{
if (millis() > requestDueTime)
{
SlackProfile profile;
SlackMessage message;
if (firstStatus)
{
profile = slack.setCustomStatus("First status", ":apple:");
message = slack.postMessage("equant-test", "This is a test message... :apple2:");
}
else
{
profile = slack.setCustomStatus("And now the second status", ":apple2:");
message = slack.postMessage("equant-test", "This is a different message... :cactus:");
// There is an optional third parameter which takes a Unix timestamp for
// when this custom status expires:
// slack.setCustomStatus("I am the second status", ":v:", 1532627506);
}
firstStatus = !firstStatus;
displayProfile(profile);
requestDueTime = millis() + delayBetweenRequests;
}
}

133
examples/slack/slack.ino Normal file
View File

@ -0,0 +1,133 @@
/*
Use this program with the Apple2idIOT card and the basic programs RRAM, WRAM and CMDROT to read/write and rot13
a single string contained within the dual port ram on the card.
CA = 49664
AA = CA + 1
*/
// Load Wi-Fi library
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <Apple2Idiot.h>
#include "ArduinoSlack.h"
#include "credentials.h"
#include "ArduinoSlackCert.h"
// copy credentials.h.sample to credentials.h and edit
// so it contains your passwords and tokens.
Apple2Idiot a2i = Apple2Idiot();
WiFiClientSecure client;
ArduinoSlack slack(client, SLACK_ACCESS_TOKEN);
#define COMMAND_SET_CHANNEL 200
#define COMMAND_SEND_MESSAGE 201
#define COMMAND_GET_CHANNEL_MESSAGES 202
char wifi_ssid[] = WIFI_SSID; // your network SSID (name)
char wifi_password[] = WIFI_PASSWORD; // your network password
/*******************/
/* Misc */
/*******************/
const long readLoopInterval = 100; // millis
unsigned long lastReadLoopTime = 0;
byte lastAppleCommand = 0;
/*################################################
# Setup #
################################################*/
void setup() {
Serial.begin(115200);
a2i.init();
Serial.println("");
Serial.println("Starting wifi...");
Serial.print(" connecting to: ");
Serial.println(wifi_ssid);
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(600);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected successfully");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP()); //Show ESP32 IP on serial
client.setCACert(slack_server_cert);
Serial.println("Setup done");
}
/*################################################
# Functions #
################################################*/
/*################################################
# Main #
################################################*/
//String channel_name = "equant-test";
String channel_name = "C02EAQECY5A";
String message = "";
SlackConvoHist conversation;
void loop() {
if ((millis() - lastReadLoopTime) > readLoopInterval) {
byte command_byte = a2i.read_data(APPLE_COMMAND_ADDRESS);
if (command_byte == RAM_BUSY) {
Serial.println("Command Read: RAM BUSY");
}
else if (command_byte != lastAppleCommand){
byte result = 0;
Serial.print("Command Switch command_byte: ");
Serial.println(command_byte);
switch(command_byte) {
case COMMAND_GET_CHANNEL_MESSAGES:
Serial.println("COMMAND_GET_CHANNEL_MESSAGES");
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
char cc[250];
channel_name.toCharArray(cc, 250);
conversation = slack.conversationHistory(cc, "2");
serializeJsonPretty(conversation.messageObj, Serial);
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte
break;
case COMMAND_SET_CHANNEL:
Serial.println("COMMAND_SET_CHANNEL");
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
channel_name = a2i.read_string_from_ram(SHARED_RAM_START_ADDRESS);
Serial.println("Received: ["+channel_name+"]");
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte
break;
case COMMAND_SEND_MESSAGE:
Serial.println("COMMAND_SEND_MESSAGE:");
SlackMessage slackMessage;
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
message = a2i.read_string_from_ram(SHARED_RAM_START_ADDRESS);
char m[250];
message.toCharArray(m, 250);
char c[250];
channel_name.toCharArray(c, 250);
slackMessage = slack.postMessage(c, m);
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, result); // notify Apple IIe we are done processing command byte
break;
}
lastAppleCommand = command_byte;
}
lastReadLoopTime = millis();
}
}

177
examples/weather/; Normal file
View File

@ -0,0 +1,177 @@
/*
Use this program with the Apple2idIOT card and the basic programs RRAM, WRAM and CMDROT to read/write and rot13
a single string contained within the dual port ram on the card.
CA = 49664
AA = CA + 1
*/
// Load Wi-Fi library
#include <WiFi.h>
#include <Apple2Idiot.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
Apple2Idiot a2i = Apple2Idiot();
#define COMMAND_SET_COUNTRY 200
#define COMMAND_SET_CITY 201
#define COMMAND_FETCH_WEATHER 205
const char* wifi_ssid = "GSO";
const char* wifi_password = "xerxes27";
/*******************/
/* Weather Service */
/*******************/
const String weather_service_api_key= "0ab97bbbea58592d7c9d64067a34d2d0";
const String weather_url = "http://api.openweathermap.org/data/2.5/weather?";
/*******************/
/* Misc */
/*******************/
const long readLoopInterval = 100; // millis
unsigned long lastReadLoopTime = 0;
byte lastAppleCommand = 0;
/*################################################
# Setup #
################################################*/
void setup() {
Serial.begin(115200);
a2i.init();
Serial.println("");
Serial.println("Starting wifi...");
Serial.print(" connecting to: ");
Serial.println(wifi_ssid);
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(600);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected successfully");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP()); //Show ESP32 IP on serial
Serial.println("Setup done");
}
/*################################################
# Functions #
################################################*/
byte fetch_weather(char* city_name) {
byte result = 0;
HTTPClient http;
const String request_url = weather_url + "q=Tucson,us&APPID=" + weather_service_api_key;
Serial.println(request_url);
http.begin(request_url);
int httpCode = http.GET(); //Make the request
delay(10);
if (httpCode > 0) { //Check for the returning code
String payload = http.getString();
//Serial.println(httpCode);
Serial.println("++++++++++++++++++++++++");
Serial.println(payload);
Serial.println("++++++++++++++++++++++++");
StaticJsonDocument<200> filter;
filter["weather"][0]["main"] = true;
filter["weather"][0]["description"] = true;
filter["main"]["humidity"] = true;
filter["main"]["temp"] = true;
filter["wind"]["speed"] = true;
filter["wind"]["deg"] = true;
StaticJsonDocument<400> doc;
DeserializationError error = deserializeJson(doc, payload, DeserializationOption::Filter(filter));
//DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
} else {
//const char* main = doc["main"];
Serial.println("----------------------");
serializeJsonPretty(doc, Serial);
Serial.println("----------------------");
serializeJsonPretty(doc["wind"], Serial);
Serial.println("----------------------");
//int temp = doc["main"]["temp"];
//int humidity = doc["main"]["humidity"];
//float wind_speed = doc["wind"]["speed"];
//int wind_deg = doc["wind"]["deg"];
//String weather_description = doc["weather"][0]["description"];
//a2i.write_data(0, round(temp-273.15));
//a2i.write_data(1, round(humidity));
//a2i.write_data(2, round(wind_deg/10)); // divide by twn because 360 is too big for 8 bits
//a2i.write_data(3, round(wind_speed));
String temp = doc["main"]["temp"];
String humidity = doc["main"]["humidity"];
String wind_speed = doc["wind"]["speed"];
String wind_deg = doc["wind"]["deg"];
String weather_description = doc["weather"][0]["description"];
int address_counter = a2i.write_string_to_shared_ram(temp, SHARED_RAM_START_ADDRESS);
address_counter = a2i.write_string_to_shared_ram(humidity, address_counter + 1);
address_counter = a2i.write_string_to_shared_ram(wind_speed, address_counter + 1);
address_counter = a2i.write_string_to_shared_ram(wind_deg, address_counter + 1);
}
result = ACK;
}
else {
Serial.println("Error on HTTP request");
result = ERR;
}
http.end(); //Free the resources
return result;
}
/*################################################
# Main #
################################################*/
void loop() {
if ((millis() - lastReadLoopTime) > readLoopInterval) {
byte command_byte = a2i.read_data(APPLE_COMMAND_ADDRESS);
if (command_byte == RAM_BUSY) {
Serial.println("Command Read: RAM BUSY");
}
else if (command_byte != lastAppleCommand){
byte result = 0;
Serial.print("Command Switch command_byte: ");
Serial.println(command_byte);
switch(command_byte) {
case COMMAND_SET_COUNTRY:
//a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
//int access_point_n = a2i.read_data(SHARED_RAM_START_ADDRESS) - 1;
//String password_as_String = a2i.read_string_from_ram(SHARED_RAM_START_ADDRESS+1);
//a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
break;
case COMMAND_SET_CITY:
//a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
//int access_point_n = a2i.read_data(SHARED_RAM_START_ADDRESS) - 1;
//String password_as_String = a2i.read_string_from_ram(SHARED_RAM_START_ADDRESS+1);
//a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
break;
case COMMAND_FETCH_WEATHER:
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
result = fetch_weather("Tucson");
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, result); // notify Apple IIe we are done processing command byte
break;
}
lastAppleCommand = command_byte;
}
lastReadLoopTime = millis();
}
}

View File

@ -1,194 +1,44 @@
/*
Use this program with the Apple2idIOT card and the basic programs RRAM, WRAM and CMDROT to read/write and rot13
a single string contained within the dual port ram on the card.
CA = 49664
AA = CA + 1
*/
#define DEBUG true
// Load Wi-Fi library
#include <WiFi.h>
#include <Apple2Idiot.h>
#include <ArduinoJson.h>
#include <HTTPClient.h>
/**********************/
/* Default WiFi */
/**********************/
Apple2Idiot a2i = Apple2Idiot();
// Replace with your network credentials
const char* wifi_ssid = "ella";
const char* wifi_password = "";
#define COMMAND_SET_COUNTRY 200
#define COMMAND_SET_CITY 201
#define COMMAND_FETCH_WEATHER 205
/**************/
/* ESP32 Pins */
/**************/
const char* wifi_ssid = "GSO";
const char* wifi_password = "xerxes27";
/* DATA BUS (numbers mean GPIO port) */
#define D0R 12
#define D1R 13
#define D2R 14
#define D3R 15
#define D4R 16
#define D5R 17
#define D6R 18
#define D7R 19
byte data_pins[] = {D7R, D6R, D5R, D4R, D3R, D2R, D1R, D0R};
#define DATA_BUS_SIZE 8
/*******************/
/* Weather Service */
/*******************/
/* Address Bus */
#define A0R 21
#define A1R 22
#define A2R 23
#define A3R 25
#define A4R 26
#define A5R 27
#define A6R 32
#define A7R 33
byte address_pins[] = {A0R, A1R, A2R, A3R, A4R, A5R, A6R, A7R};
#define ADDRESS_BUS_SIZE 8
#define ESP_COMMAND_ADDRESS 0
#define APPLE_COMMAND_ADDRESS 1
#define SHARED_RAM_START_ADDRESS 2
#define RAM_BUSY 666
#define MAX_STR_LEN 44 // arbitrary length.
const String weather_service_api_key= "0ab97bbbea58592d7c9d64067a34d2d0";
const String weather_url = "http://api.openweathermap.org/data/2.5/weather?";
#define COMMAND_GENERIC 0b10000000
#define COMMAND_SCAN_WIFI 111
#define COMMAND_CONNECT 112
String country_code = "US";
String city_name = "Tucson";
/* IDT7132S dual port ram chip enable */
#define RW_PIN 5
#define RW_WRITE LOW
#define RW_READ HIGH
#define INPUT_35 35
#define INPUT_34 34
#define ETX 3 // ASCII "End of Text" (ETX) character
#define ACK 0b00000110 // Acknowledge
#define EOT 0b00000100 // End of transmit
/*********/
/* Misc. */
/*********/
/*******************/
/* Misc */
/*******************/
const long readLoopInterval = 100; // millis
unsigned long lastReadLoopTime = 0;
byte ram[256];
volatile byte ram_busy=0;
byte lastAppleCommand = 0;
/*################################################
# Functions #
################################################*/
void unbusy_ram() {
set_address(ESP_COMMAND_ADDRESS);
ram_busy = false;
}
boolean set_address(int address) {
if (ram_busy) {
Serial.println("BUSY");
return false;
}
ram_busy = true;
for (byte i=0; i<ADDRESS_BUS_SIZE; i++) {
byte state = bitRead(address, i);
digitalWrite(address_pins[i], state);
}
return true;
}
byte read_data(int address) {
byte data_bus_read = 0;
if (set_address(address)) {
digitalWrite(RW_PIN, RW_READ); // Should already be set to RW_READ, but just in case.
for (byte i=0; i<DATA_BUS_SIZE; i++) {
byte pin_state = digitalRead(data_pins[i]);
data_bus_read += pin_state * pow(2,i);
}
ram_busy = false;
unbusy_ram();
return data_bus_read;
} else {
return RAM_BUSY;
}
}
boolean write_data(byte address, byte byte_to_write) {
Serial.print("WRITE: ");
Serial.print(byte_to_write);
Serial.print(" -> ");
Serial.println(address);
if (set_address(address)) {
//set_address(address);
Serial.print(" D:");
for (byte i=0; i<DATA_BUS_SIZE; i++) {
byte bit_to_write = (byte_to_write >> i) & 0b00000001;
pinMode(data_pins[i], OUTPUT);
digitalWrite(data_pins[i], bit_to_write);
Serial.print(bit_to_write);
}
Serial.println();
digitalWrite(RW_PIN, RW_WRITE);
delay(1);
digitalWrite(RW_PIN, RW_READ);
for (byte i=0; i<DATA_BUS_SIZE; i++) {
pinMode(data_pins[i], INPUT);
}
unbusy_ram();
return true;
} else {
return false;
}
}
unsigned int write_string_to_shared_ram(String string_to_send, unsigned int address) {
//if (string_to_send.length() > 15 - 1) { // - 1 because of null character at end of string.
write_data(ESP_COMMAND_ADDRESS, 12);
unsigned int c = 0;
for (c=0; c < string_to_send.length(); c++) {
Serial.print("A(");
Serial.print(c);
Serial.print("): ");
Serial.println(string_to_send[c]);
write_data(address+c, string_to_send[c]);
}
write_data(address+c, ETX);
//write_data(15, COMMAND_FROM_ESP + command_message + COMMAND_NO_DATA_WAITING);
write_data(ESP_COMMAND_ADDRESS, 27);
return address+c;
}
String read_string_from_ram(int address) {
byte c = 0;
int i = 0;
String read_string = "";
while ( (i<MAX_STR_LEN) && (c!=ETX) ) {
c = read_data(address+i);
read_string = read_string + char(c);
i++;
}
Serial.print("READ STRING:");
Serial.println(read_string);
return read_string;
}
void read_ram(int size_to_read=256);
void read_ram(int size_to_read) {
for (int i=0; i < size_to_read; i++) {
unsigned int foo = read_data(i);
ram[i] = foo;
Serial.print(i);
Serial.print(" ");
Serial.println(foo);
}
}
/*################################################
# Setup #
################################################*/
@ -196,27 +46,7 @@ void read_ram(int size_to_read) {
void setup() {
Serial.begin(115200);
/* Configure ESP32 Pins */
pinMode(A0R, OUTPUT); digitalWrite(A0R, LOW);
pinMode(A1R, OUTPUT); digitalWrite(A1R, LOW);
pinMode(A2R, OUTPUT); digitalWrite(A2R, LOW);
pinMode(A3R, OUTPUT); digitalWrite(A3R, LOW);
pinMode(A4R, OUTPUT); digitalWrite(A4R, LOW);
pinMode(A5R, OUTPUT); digitalWrite(A5R, LOW);
pinMode(A6R, OUTPUT); digitalWrite(A6R, LOW);
pinMode(A7R, OUTPUT); digitalWrite(A7R, LOW);
pinMode(D0R, INPUT);
pinMode(D1R, INPUT);
pinMode(D2R, INPUT);
pinMode(D3R, INPUT);
pinMode(D4R, INPUT);
pinMode(D5R, INPUT);
pinMode(D6R, INPUT);
pinMode(D7R, INPUT);
pinMode(RW_PIN, OUTPUT); digitalWrite(RW_PIN, RW_READ);
pinMode(INPUT_34, INPUT);
pinMode(INPUT_35, INPUT);
a2i.init();
Serial.println("");
Serial.println("Starting wifi...");
@ -225,7 +55,7 @@ void setup() {
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(900);
delay(600);
Serial.print(".");
}
@ -234,117 +64,115 @@ void setup() {
Serial.print("Got IP: ");
Serial.println(WiFi.localIP()); //Show ESP32 IP on serial
// Set WiFi to station mode and disconnect from an AP if it was previously connected
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(10);
write_string_to_shared_ram("Starting Up!", SHARED_RAM_START_ADDRESS);
write_data(ESP_COMMAND_ADDRESS, ACK);
write_data(APPLE_COMMAND_ADDRESS, ACK);
Serial.println("Setup done");
}
/*################################################
# Functions #
################################################*/
byte fetch_weather() {
byte result = 0;
HTTPClient http;
//const String request_url = weather_url + "q=Tucson,us&APPID=" + weather_service_api_key;
const String request_url = weather_url + "q=" + city_name + "," + country_code + "&APPID=" + weather_service_api_key;
Serial.println(request_url);
http.begin(request_url);
int httpCode = http.GET(); //Make the request
delay(10);
if (httpCode > 0) { //Check for the returning code
String payload = http.getString();
//Serial.println(httpCode);
Serial.println("++++++++++++++++++++++++");
Serial.println(payload);
Serial.println("++++++++++++++++++++++++");
StaticJsonDocument<200> filter;
filter["weather"][0]["main"] = true;
filter["weather"][0]["description"] = true;
filter["main"]["humidity"] = true;
filter["main"]["temp"] = true;
filter["wind"]["speed"] = true;
filter["wind"]["deg"] = true;
StaticJsonDocument<400> doc;
DeserializationError error = deserializeJson(doc, payload, DeserializationOption::Filter(filter));
//DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
} else {
Serial.println("----------------------");
serializeJsonPretty(doc, Serial);
Serial.println("----------------------");
serializeJsonPretty(doc["wind"], Serial);
Serial.println("----------------------");
String temp = doc["main"]["temp"];
String humidity = doc["main"]["humidity"];
String wind_speed = doc["wind"]["speed"];
String wind_deg = doc["wind"]["deg"];
String weather_description1 = doc["weather"][0]["main"];
String weather_description2 = doc["weather"][0]["description"];
int address_counter = a2i.write_string_to_shared_ram(temp, SHARED_RAM_START_ADDRESS);
address_counter = a2i.write_string_to_shared_ram(humidity, address_counter + 1);
address_counter = a2i.write_string_to_shared_ram(wind_speed, address_counter + 1);
address_counter = a2i.write_string_to_shared_ram(wind_deg, address_counter + 1);
address_counter = a2i.write_string_to_shared_ram(weather_description1, address_counter + 1);
address_counter = a2i.write_string_to_shared_ram(weather_description2, address_counter + 1);
}
result = ACK;
}
else {
Serial.println("Error on HTTP request");
result = ERR;
}
http.end(); //Free the resources
return result;
}
/*################################################
# Main #
################################################*/
void scan_wifi() {
Serial.println("scan start");
// WiFi.scanNetworks will return the number of networks found
int n = WiFi.scanNetworks();
Serial.println("scan done");
int address_counter = SHARED_RAM_START_ADDRESS + 1;
if (n == 0) {
Serial.println("no networks found");
write_data(0, address_counter);
} else {
Serial.print(n);
Serial.println(" networks found");
write_data(n, address_counter);
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print(" (");
Serial.print(WiFi.RSSI(i));
Serial.print(")");
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
delay(10);
address_counter = write_string_to_shared_ram(WiFi.SSID(i), address_counter + 1);
//address_counter = write_string_to_shared_ram(String(WiFi.RSSI(i)), address_counter + 1);
//address_counter = write_string_to_shared_ram(WiFi.encryptionType(i), address_counter + 1);
}
}
write_data(ESP_COMMAND_ADDRESS, EOT);
Serial.println("");
}
void store_ip_to_ram(byte offset) {
IPAddress ip_address = WiFi.localIP();
//for (int i=0; i < ADDRESS_BUS_SIZE; i++) {
for (int i=0; i < 4; i++) {
write_data(i+offset, ip_address[i]);
}
}
const char* wifi_ssid = "";
const char* wifi_password = "";
void loop() {
if ((millis() - lastReadLoopTime) > readLoopInterval) {
byte command_byte = read_data(APPLE_COMMAND_ADDRESS);
byte command_byte = a2i.read_data(APPLE_COMMAND_ADDRESS);
if (command_byte == RAM_BUSY) {
Serial.println("Command Read: RAM BUSY");
}
else if (command_byte != lastAppleCommand){
byte result = 0;
Serial.print("Command Switch command_byte: ");
Serial.println(command_byte);
switch(command_byte) {
case COMMAND_SCAN_WIFI:
write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
//String read_string = read_string_from_ram(SHARED_RAM_START_ADDRESS);
//write_string_to_shared_ram(rot13(read_string), SHARED_RAM_START_ADDRESS);
scan_wifi();
write_data(APPLE_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
case COMMAND_SET_COUNTRY:
Serial.println("COMMAND_SET_COUNTRY");
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
country_code = a2i.read_string_from_ram(SHARED_RAM_START_ADDRESS);
Serial.println("Received: ["+country_code+"]");
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte
break;
}
switch(command_byte) {
case COMMAND_CONNECT:
write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
int access_point_n = read_data(SHARED_RAM_START_ADDRESS);
String ssid_as_String = WiFi.SSID(access_point_n);
String password_as_String = read_string_from_ram(SHARED_RAM_START_ADDRESS+1);
wifi_password = password_as_String.c_str();
wifi_ssid = ssid_as_String.c_str();
//wifi_password = read_string_from_ram(SHARED_RAM_START_ADDRESS+1);
//wifi_ssid = WiFi.SSID(access_point_n);
//WiFi.begin(wifi_ssid, wifi_password);
Serial.print("password:");
Serial.println(password_as_String);
Serial.print("password:(");
Serial.print(wifi_password);
Serial.println(")");
//WiFi.begin(wifi_ssid, wifi_password);
WiFi.begin("GSO", "xerxes27");
while (WiFi.status() != WL_CONNECTED) {
delay(300);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected successfully");
Serial.print("Got IP: ");
Serial.println(WiFi.localIP()); //Show ESP32 IP on serial
store_ip_to_ram(SHARED_RAM_START_ADDRESS);
write_data(APPLE_COMMAND_ADDRESS, ACK);
case COMMAND_SET_CITY:
Serial.println("COMMAND_SET_CITY");
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
city_name = a2i.read_string_from_ram(SHARED_RAM_START_ADDRESS);
Serial.println("Received: ["+city_name+"]");
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte
break;
case COMMAND_FETCH_WEATHER:
Serial.println("COMMAND_FETCH_WEATHER");
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
result = fetch_weather();
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK);
a2i.write_data(ESP_COMMAND_ADDRESS, result); // notify Apple IIe we are done processing command byte
break;
}
lastAppleCommand = command_byte;
write_data(ESP_COMMAND_ADDRESS, EOT); // notify Apple IIe we are done processing command byte
}
lastReadLoopTime = millis();
}

View File

@ -79,14 +79,12 @@ void loop() {
Serial.print("Command Switch command_byte: ");
Serial.println(command_byte);
switch(command_byte) {
case COMMAND_SCAN_WIFI:
case COMMAND_WIFI_SCAN:
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
scan_wifi();
a2i.write_data(APPLE_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
break;
}
switch(command_byte) {
case COMMAND_CONNECT:
case COMMAND_WIFI_CONNECT:
a2i.write_data(ESP_COMMAND_ADDRESS, ACK); // notify Apple IIe we are processing command byte
int access_point_n = a2i.read_data(SHARED_RAM_START_ADDRESS) - 1;
String ssid_as_String = WiFi.SSID(access_point_n);

View File

@ -43,7 +43,7 @@ void Apple2Idiot::unbusy_ram() {
boolean Apple2Idiot::set_address(int address) {
if (ram_busy) {
Serial.println("BUSY");
//Serial.println("BUSY");
return false;
}
ram_busy = true;
@ -78,14 +78,14 @@ boolean Apple2Idiot::write_data(byte address, byte byte_to_write) {
Serial.println(address);
if (set_address(address)) {
//set_address(address);
Serial.print(" D:");
//Serial.print(" D:");
for (byte i=0; i<DATA_BUS_SIZE; i++) {
byte bit_to_write = (byte_to_write >> i) & 0b00000001;
pinMode(data_pins[i], OUTPUT);
digitalWrite(data_pins[i], bit_to_write);
Serial.print(bit_to_write);
//Serial.print(bit_to_write);
}
Serial.println();
//Serial.println();
digitalWrite(RW_PIN, RW_WRITE);
delay(1);
digitalWrite(RW_PIN, RW_READ);
@ -123,7 +123,9 @@ String Apple2Idiot::read_string_from_ram(int address) {
String read_string = "";
while ( (i<MAX_STR_LEN) && (c!=ETX) ) {
c = read_data(address+i);
read_string = read_string + char(c);
if (c!=ETX) {
read_string = read_string + char(c);
}
i++;
}
Serial.print("READ STRING:");

View File

@ -23,15 +23,31 @@
#define A6R 32
#define A7R 33
#define ADDRESS_BUS_SIZE 8
#define ESP_COMMAND_ADDRESS 0
#define APPLE_COMMAND_ADDRESS 1
#define SHARED_RAM_START_ADDRESS 2
#define RAM_BUSY 666
#define MAX_STR_LEN 44 // arbitrary length.
#define MAX_STR_LEN 250 // arbitrary length.
#define COMMAND_GENERIC 0b10000000
#define COMMAND_SCAN_WIFI 111
#define COMMAND_CONNECT 112
//
// Commands and messages that are communicated between the ESP and the Apple
// via the dual-port ram.
//
// Commands >=200 are "non-reserved", and are meant
// to be defined within scipts that use this library.
// So anything in this library should be below 200.
//
// If the values of these defines are changed, then they will need to be
// updated within the code that runs on the Apple as well.
//#define COMMAND_GENERIC 0b10000000
#define COMMAND_WIFI_SCAN 111
#define COMMAND_WIFI_CONNECT 112
#define ACK 0b00000110 // Acknowledge
#define EOT 0b00000100 // End of transmit
#define ERR 0b00000101 // Error
#define MORE_TO_SEND 0b00000111
/* IDT7132S dual port ram chip enable */
#define RW_PIN 5
@ -39,8 +55,6 @@
#define RW_READ HIGH
#define ETX 3 // ASCII "End of Text" (ETX) character
#define ACK 0b00000110 // Acknowledge
#define EOT 0b00000100 // End of transmit
#define INPUT_35 35
#define INPUT_34 34

View File

@ -13,6 +13,10 @@ DONE: Add diode to +5V.
Move ESP32 towards keyboard to accomodate USB
Software reset of ESP32
## Potential Trivial Features
+ Gives each computer a unique ID (MAC address)
+ Prodos driver for NPT time.
## Thanks