In the Video
Japanese【日本語】
English【英語】
1. Overview
We are making smart remote controls. This is the final of the 7 sessions, and we will be able to operate home appliances from the outside using a smartphone and link with an AI speaker.
Any home appliance that can be controlled by a normal remote control, such as lighting, TVs, and air conditioners, can be used.
For development, we will use Arduino and ESP32 to realize it.
2. Overall flow of Smart Remote Controller Production
We will eventually create a smart remote control, and we will distribute it in a total of 7 posts.
This is the 6th “Home appliance remote control with a smartphone”.
No | Item | Content | Hard | Soft | Note |
---|---|---|---|---|---|
1 | Overall flow, system configuration, items used, reasons for selection, development environment, etc. | – | – | Another Post | |
2 | Green LED | Learn the basics for beginners. We will make “L blinking” that lights up and blinks the LED. | 〇 | 〇 | |
3 | Infrared receiving sensor | Description of infrared receiving sensor Schematic to Wiring, Software | 〇 | 〇 | |
4 | Infrared transmission LED | Infrared transmission LED description Schematic to Wiring, Software | 〇 | 〇 | |
5 | LED operation with smartphone(at home) | We will create software to operate the LED with smartphone. (Web server function, SPIFFS operation) | – | 〇 | |
6 | Remote control with smartphone(at home) | We will create software that to operate the remote control with smartphone indoors. (Button name, signal save/read) | – | 〇 | |
7 | Operate from outside And AI speaker cooperation | We will create software to operate the remote control with smartphone from the outdoors, and AI speaker cooperation. | – | 〇 | This Post |
3. The development environment
Arduino was developed in Italy under the philosophy of “making things easier and easier to understand”.
Currently, it is widely used for learning all over the world, and the library is also substantial.
So, if you want to start electronic work, I think this is the only development environment.
Therefore, I use Arduino at this time.
4. Software
All program files are put together in this zip file. Please download as needed.
Listed below are the 4 files of the Arduino program in this zip file.
(HTML and Javascript are in the zip file.)
//*************************************************************************
// SmartRemocon Ver2023.2.2
// Arduino board : ESP32(Arduino core for the ESP32) by Espressif Systems ver 2.0.6
// Written by IT-Taro
//***********************************************************************
// Load library and Config file
#include <EEPROM.h>
#include <SPIFFS.h>
#include <ESPAsyncWebServer.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <ArduinoJson.h> // Version[5.13.5]
#include "config.h"
// for WebServer
AsyncWebServer webServer ( 80 );
// Declare the type (structure) used in EEPROM (the type that saves the button name)
struct st_remocon {
char remo_name[65];
};
// For saving remote control data (1500 unsigned short type arrays)
unsigned short irData[1500];
// Valuable
bool ledFlag = true; // LED control flag
// MQTT connection ON/OFF (false when OFF)
bool MQTT_CONNECT = true;
// for MQTT client
WiFiClientSecure wifiClient; // for MQTT
PubSubClient mqttClient(host, 8883, wifiClient);
// First thing to do when booting
void setup(void) {
// Serial setting
Serial.begin(115200);
Serial.println ( );
// SPIFFS start
SPIFFS.begin();
// Start EEPROM (Size specification: 10 button names [65*10])
EEPROM.begin(650);
// pin mode setting
pinMode ( LED_PIN, OUTPUT );
pinMode ( IR_R_PIN, INPUT );
pinMode ( IR_S_PIN, OUTPUT );
// Wi-Fi connection processing
setup_wifi();
// WebServer setting process
setup_webserver();
Serial.println("##### setup completed! #####");
}
// Wireless Wi-Fi settings
void setup_wifi() {
// Wireless Wi-Fi information setting
Serial.println ( "Wi-Fi SetUp" );
WiFi.config( ip, gateway, subnet, DNS ); // MQTT connection is not possible without DNS
WiFi.begin ( ssid, password );
// Wi-Fi connection processing (infinite loop until connected)
while ( WiFi.status() != WL_CONNECTED ) {
// Blink the LED every 1 second
ledFlag = !ledFlag;
digitalWrite(LED_PIN, ledFlag);
// Wait for 1 second
delay ( 1000 );
Serial.print ( "." );
}
// Since the Wi-Fi connection was established, the IP address is displayed on the serial monitor
Serial.print ( "Wi-Fi Connected! IP address: " );
Serial.println ( WiFi.localIP() );
// LED lighting (Wi-Fi connection status)
digitalWrite ( LED_PIN, true );
// ------- Set CA certificate to client only when MQTT_CONNECT is ON -------
if ( MQTT_CONNECT ) {
wifiClient.setCACert(beebottle_ca_cert);
}
}
// After setup is complete, repeat processing until power is turned off
void loop(void){
// ------- only if MQTT_CONNECT is ON -------
if ( MQTT_CONNECT ) {
// Check the MQTT status and process the MQTT connection if it is not connected
if ( !mqttClient.connected() ) {
reconnect();
}
// MQTT client processing
mqttClient.loop();
}
}
// MQTT connection process
void reconnect() {
// loop until MQTT connection state
while (!mqttClient.connected()) {
Serial.println("Attempting MQTT connection...");
// MQTT setting information definition
String username = "token:";
username += channelToken;
// MQTT connection process
mqttClient.connect(clientID, username.c_str(), NULL);
delay(2000);
}
Serial.println("MQTT connected");
// Process setting when receiving MQTT message
mqttClient.setCallback(callback);
// Configure TOPIC to receive MQTT messages
mqttClient.subscribe(topic);
}
// Processing when MQTT message is received
void callback(char* topic, byte* payload, unsigned int length) {
// save MQTT received message in variable
char recvData[MQTT_MAX_PACKET_SIZE];
snprintf(recvData, sizeof(recvData), "%s", payload);
// Display MQTT received message on serial monitor
Serial.print("Message arrived [");
Serial.print(topic);
Serial.println("] ");
Serial.println(recvData);
// Parse the JSON format of the received data and save it to a variable
StaticJsonBuffer<MQTT_MAX_PACKET_SIZE> jsonBuffer;
JsonObject& jsonBuf = jsonBuffer.parseObject(recvData);
// If JSON format parsing is not successful, display an error and exit
if (!jsonBuf.success()) {
Serial.println("parseObject() failed");
return;
}
// Acquire and save the received data (data)
const char* parsedPayload = jsonBuf["data"];
// Determine if received data (data) exists
if (parsedPayload != NULL) {
Serial.print("payload: ");
Serial.println(parsedPayload);
// Transmit remote control number in receive data (data)
if (contRemocon( parsedPayload )) {
Serial.println("MQTT send OK");
} else {
Serial.println("MQTT send NG");
}
}
}
// pin arrangement etc.
const byte LED_PIN = 22; // green LED
const byte IR_R_PIN = 23; // Infrared reception
const byte IR_S_PIN = 32; // Infrared transmission(IO 34,35 does not work)
// Wi-Fi settings
const char *ssid = "##### SSID #####";
const char *password = "### PASSWORD ###";
IPAddress ip(192, 168, 1, 123); // IP address (IP used by this machine)
IPAddress gateway(192, 168, 1, 1); // default gateway
IPAddress subnet(255, 255, 255, 0); // sub-net mask
IPAddress DNS(192, 168, 1, 1); // DNS server
// Beebotte settting
const char *host = "mqtt.beebotte.com";
const char *clientID = "esp32_001";
const char *channelToken = "##### TOKEN #####";
const char *topic = "##### CHANNEL #####/resource1";
// The certificate is changed periodically, so it needs to be updated from time to time
// Use this for download ca
https://beebotte.com/certs/mqtt.beebotte.com.pem
const char *beebottle_ca_cert = \
"-----BEGIN CERTIFICATE-----\n" \
"MIIGQzCCBSugAwIBAgIQS+4z5c2Vxpa/089CrjLxRDANBgkqhkiG9w0BAQsFADCB\n" \
"jzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n" \
"A1UEBxMHU2FsZm9yZDEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQD\n" \
"Ey5TZWN0aWdvIFJTQSBEb21haW4gVmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENB\n" \
"MB4XDTIyMDgwNTAwMDAwMFoXDTIzMDgwNTIzNTk1OVowHDEaMBgGA1UEAxMRbXF0\n" \
"dC5iZWVib3R0ZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2\n" \
"HOgaiUNPgq4weQWnEARZZ15YSmJGos+6OfaJ+rfU7xN2TNIseLMDFyor6ehKKBfF\n" \
"weB/EBQJOlq4p48KbIyY9pqZLa6QoUr+2l06hkyEro4nbj/w4VWee+RccgvV1w5A\n" \
"nGm6BRzQTd2zZVBkCnKLdlBYWfqdWJAzODh+AGXCNJ06oE3W9rurh0Xorh8jmZcI\n" \
"ihfkfW14EC9nx0gNMxOTKYW6w8k+SsOcp6jOd2tNFmm/RZlpUkM+CwGXrn8WbrmF\n" \
"/KAwRboVw/DCtPmvg4ItrylQNjAZ2eY1UqI9SuaDzRPUhghYWWZthJoeuXsb7ps+\n" \
"wp8S9jHUw9ry/uyO0V6NAgMBAAGjggMLMIIDBzAfBgNVHSMEGDAWgBSNjF7EVK2K\n" \
"4Xfpm/mbBeG4AY1h4TAdBgNVHQ4EFgQU3Ysg/3l33FxDdSjtpWo9sTdAI7EwDgYD\n" \
"VR0PAQH/BAQDAgWgMAwGA1UdEwEB/wQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEG\n" \
"CCsGAQUFBwMCMEkGA1UdIARCMEAwNAYLKwYBBAGyMQECAgcwJTAjBggrBgEFBQcC\n" \
"ARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EMAQIBMIGEBggrBgEFBQcB\n" \
"AQR4MHYwTwYIKwYBBQUHMAKGQ2h0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGln\n" \
"b1JTQURvbWFpblZhbGlkYXRpb25TZWN1cmVTZXJ2ZXJDQS5jcnQwIwYIKwYBBQUH\n" \
"MAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMDMGA1UdEQQsMCqCEW1xdHQuYmVl\n" \
"Ym90dGUuY29tghV3d3cubXF0dC5iZWVib3R0ZS5jb20wggF/BgorBgEEAdZ5AgQC\n" \
"BIIBbwSCAWsBaQB3AK33vvp8/xDIi509nB4+GGq0Zyldz7EMJMqFhjTr3IKKAAAB\n" \
"gm1H8UwAAAQDAEgwRgIhAPSkokAAmuuFPQcLwnzmklGV2LG18nxYMszVwcshdNLd\n" \
"AiEAnXonMoYJ3woym5ZklDq3llrzShulgbijU3hWTJMiZwQAdgB6MoxU2LcttiDq\n" \
"OOBSHumEFnAyE4VNO9IrwTpXo1LrUgAAAYJtR/EYAAAEAwBHMEUCIQC0VI4khyqj\n" \
"laTjeNNGs61TOX7n1S6qE1FuIXFsGkuCAwIgaxzXXL/HPqBUGUb7EDoD+yOy0l8p\n" \
"UZTgY5hS5bnj/dAAdgDoPtDaPvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAA\n" \
"AYJtR/DlAAAEAwBHMEUCIQDKDk5MX6omMZXNdxrLUhkKUwm2yWaC2Rzqh9yuGb9k\n" \
"2wIgeRqzIClbgHNA0oK9lL3RYU2CFih434PamgZsTGupkuMwDQYJKoZIhvcNAQEL\n" \
"BQADggEBALrM4n2WfCJpuF74zZPFqfVmYgsgnlcFzUjL7Dom3dxXb1sGkQ8vukjW\n" \
"NnLQ64FlZEvz5OJotZeuikzBwyjfFNyfKSFElBr/zqdW7Mp8RcYoa6zOG2+T/GA2\n" \
"C3Xesg4WBFvQ8DjZFobZiICuI5gZ8m/MuMMX6JXobMDa/9p0ECOxsCfeOesnLNJu\n" \
"h05h3PLkLxRgHROSIp3hoUtN1vGnW2cJBLr29uu7utTjgRX514IRqefFwEovOb3Z\n" \
"DcWcxkpcMM7Z26UoNO2V2d7kyuQgXq3RwePm3a8v5Pp7Fy8goGIrnfoLPhX8QCX3\n" \
"Pz3SiPLrMq9IpjzlAOP0wwu6QLSwUXQ=\n" \
"-----END CERTIFICATE-----\n" \
"-----BEGIN CERTIFICATE-----\n" \
"MIIGEzCCA/ugAwIBAgIQfVtRJrR2uhHbdBYLvFMNpzANBgkqhkiG9w0BAQwFADCB\n" \
"iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" \
"cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" \
"BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTgx\n" \
"MTAyMDAwMDAwWhcNMzAxMjMxMjM1OTU5WjCBjzELMAkGA1UEBhMCR0IxGzAZBgNV\n" \
"BAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEYMBYGA1UE\n" \
"ChMPU2VjdGlnbyBMaW1pdGVkMTcwNQYDVQQDEy5TZWN0aWdvIFJTQSBEb21haW4g\n" \
"VmFsaWRhdGlvbiBTZWN1cmUgU2VydmVyIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
"AQ8AMIIBCgKCAQEA1nMz1tc8INAA0hdFuNY+B6I/x0HuMjDJsGz99J/LEpgPLT+N\n" \
"TQEMgg8Xf2Iu6bhIefsWg06t1zIlk7cHv7lQP6lMw0Aq6Tn/2YHKHxYyQdqAJrkj\n" \
"eocgHuP/IJo8lURvh3UGkEC0MpMWCRAIIz7S3YcPb11RFGoKacVPAXJpz9OTTG0E\n" \
"oKMbgn6xmrntxZ7FN3ifmgg0+1YuWMQJDgZkW7w33PGfKGioVrCSo1yfu4iYCBsk\n" \
"Haswha6vsC6eep3BwEIc4gLw6uBK0u+QDrTBQBbwb4VCSmT3pDCg/r8uoydajotY\n" \
"uK3DGReEY+1vVv2Dy2A0xHS+5p3b4eTlygxfFQIDAQABo4IBbjCCAWowHwYDVR0j\n" \
"BBgwFoAUU3m/WqorSs9UgOHYm8Cd8rIDZsswHQYDVR0OBBYEFI2MXsRUrYrhd+mb\n" \
"+ZsF4bgBjWHhMA4GA1UdDwEB/wQEAwIBhjASBgNVHRMBAf8ECDAGAQH/AgEAMB0G\n" \
"A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAbBgNVHSAEFDASMAYGBFUdIAAw\n" \
"CAYGZ4EMAQIBMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly9jcmwudXNlcnRydXN0\n" \
"LmNvbS9VU0VSVHJ1c3RSU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDB2Bggr\n" \
"BgEFBQcBAQRqMGgwPwYIKwYBBQUHMAKGM2h0dHA6Ly9jcnQudXNlcnRydXN0LmNv\n" \
"bS9VU0VSVHJ1c3RSU0FBZGRUcnVzdENBLmNydDAlBggrBgEFBQcwAYYZaHR0cDov\n" \
"L29jc3AudXNlcnRydXN0LmNvbTANBgkqhkiG9w0BAQwFAAOCAgEAMr9hvQ5Iw0/H\n" \
"ukdN+Jx4GQHcEx2Ab/zDcLRSmjEzmldS+zGea6TvVKqJjUAXaPgREHzSyrHxVYbH\n" \
"7rM2kYb2OVG/Rr8PoLq0935JxCo2F57kaDl6r5ROVm+yezu/Coa9zcV3HAO4OLGi\n" \
"H19+24rcRki2aArPsrW04jTkZ6k4Zgle0rj8nSg6F0AnwnJOKf0hPHzPE/uWLMUx\n" \
"RP0T7dWbqWlod3zu4f+k+TY4CFM5ooQ0nBnzvg6s1SQ36yOoeNDT5++SR2RiOSLv\n" \
"xvcRviKFxmZEJCaOEDKNyJOuB56DPi/Z+fVGjmO+wea03KbNIaiGCpXZLoUmGv38\n" \
"sbZXQm2V0TP2ORQGgkE49Y9Y3IBbpNV9lXj9p5v//cWoaasm56ekBYdbqbe4oyAL\n" \
"l6lFhd2zi+WJN44pDfwGF/Y4QA5C5BIG+3vzxhFoYt/jmPQT2BVPi7Fp2RBgvGQq\n" \
"6jG35LWjOhSbJuMLe/0CjraZwTiXWTb2qHSihrZe68Zk6s+go/lunrotEbaGmAhY\n" \
"LcmsJWTyXnW0OMGuf1pGg+pRyrbxmRE1a6Vqe8YAsOf4vmSyrcjC8azjUeqkk+B5\n" \
"yOGBQMkKW+ESPMFgKuOXwIlCypTPRpgSabuY0MLTDXJLR27lk8QyKGOHQ+SwMj4K\n" \
"00u/I5sUKUErmgQfky3xxzlIPK1aEn8=\n" \
"-----END CERTIFICATE-----\n" \
"-----BEGIN CERTIFICATE-----\n" \
"MIIFgTCCBGmgAwIBAgIQOXJEOvkit1HX02wQ3TE1lTANBgkqhkiG9w0BAQwFADB7\n" \
"MQswCQYDVQQGEwJHQjEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYD\n" \
"VQQHDAdTYWxmb3JkMRowGAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UE\n" \
"AwwYQUFBIENlcnRpZmljYXRlIFNlcnZpY2VzMB4XDTE5MDMxMjAwMDAwMFoXDTI4\n" \
"MTIzMTIzNTk1OVowgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpOZXcgSmVyc2V5\n" \
"MRQwEgYDVQQHEwtKZXJzZXkgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBO\n" \
"ZXR3b3JrMS4wLAYDVQQDEyVVU0VSVHJ1c3QgUlNBIENlcnRpZmljYXRpb24gQXV0\n" \
"aG9yaXR5MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAgBJlFzYOw9sI\n" \
"s9CsVw127c0n00ytUINh4qogTQktZAnczomfzD2p7PbPwdzx07HWezcoEStH2jnG\n" \
"vDoZtF+mvX2do2NCtnbyqTsrkfjib9DsFiCQCT7i6HTJGLSR1GJk23+jBvGIGGqQ\n" \
"Ijy8/hPwhxR79uQfjtTkUcYRZ0YIUcuGFFQ/vDP+fmyc/xadGL1RjjWmp2bIcmfb\n" \
"IWax1Jt4A8BQOujM8Ny8nkz+rwWWNR9XWrf/zvk9tyy29lTdyOcSOk2uTIq3XJq0\n" \
"tyA9yn8iNK5+O2hmAUTnAU5GU5szYPeUvlM3kHND8zLDU+/bqv50TmnHa4xgk97E\n" \
"xwzf4TKuzJM7UXiVZ4vuPVb+DNBpDxsP8yUmazNt925H+nND5X4OpWaxKXwyhGNV\n" \
"icQNwZNUMBkTrNN9N6frXTpsNVzbQdcS2qlJC9/YgIoJk2KOtWbPJYjNhLixP6Q5\n" \
"D9kCnusSTJV882sFqV4Wg8y4Z+LoE53MW4LTTLPtW//e5XOsIzstAL81VXQJSdhJ\n" \
"WBp/kjbmUZIO8yZ9HE0XvMnsQybQv0FfQKlERPSZ51eHnlAfV1SoPv10Yy+xUGUJ\n" \
"5lhCLkMaTLTwJUdZ+gQek9QmRkpQgbLevni3/GcV4clXhB4PY9bpYrrWX1Uu6lzG\n" \
"KAgEJTm4Diup8kyXHAc/DVL17e8vgg8CAwEAAaOB8jCB7zAfBgNVHSMEGDAWgBSg\n" \
"EQojPpbxB+zirynvgqV/0DCktDAdBgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rID\n" \
"ZsswDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAG\n" \
"BgRVHSAAMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29t\n" \
"L0FBQUNlcnRpZmljYXRlU2VydmljZXMuY3JsMDQGCCsGAQUFBwEBBCgwJjAkBggr\n" \
"BgEFBQcwAYYYaHR0cDovL29jc3AuY29tb2RvY2EuY29tMA0GCSqGSIb3DQEBDAUA\n" \
"A4IBAQAYh1HcdCE9nIrgJ7cz0C7M7PDmy14R3iJvm3WOnnL+5Nb+qh+cli3vA0p+\n" \
"rvSNb3I8QzvAP+u431yqqcau8vzY7qN7Q/aGNnwU4M309z/+3ri0ivCRlv79Q2R+\n" \
"/czSAaF9ffgZGclCKxO/WIu6pKJmBHaIkU4MiRTOok3JMrO66BQavHHxW/BBC5gA\n" \
"CiIDEOUMsfnNkjcZ7Tvx5Dq2+UUTJnWvu6rvP3t3O9LEApE9GQDTF1w52z97GA1F\n" \
"zZOFli9d31kWTz9RvdVFGD/tSo7oBmF0Ixa1DVBzJ0RHfxBdiSprhTEUxOipakyA\n" \
"vGp4z7h/jnZymQyd/teRCBaho1+V\n" \
"-----END CERTIFICATE-----\n" \
"-----BEGIN CERTIFICATE-----\n" \
"MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\n" \
"MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\n" \
"GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\n" \
"YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\n" \
"MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n" \
"BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\n" \
"GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" \
"ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\n" \
"BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n" \
"3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\n" \
"YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\n" \
"rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\n" \
"ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\n" \
"oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\n" \
"MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\n" \
"QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\n" \
"b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\n" \
"AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n" \
"GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\n" \
"Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\n" \
"G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\n" \
"l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\n" \
"smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n" \
"-----END CERTIFICATE-----\n" \
;
// Infrared reception (signal reception or processing for 30 seconds)
void setRemocon (AsyncWebServerRequest *request) {
// Declare variables to use
unsigned short irCount = 0; // number of HIGH and LOW signals
uint32_t lastt = 0; // keep previous elapsed time
uint32_t deltt = 0; // keep previous elapsed time
uint32_t sMilli; // start time of this process
uint32_t wMilli; // Infrared wait start time
uint32_t sMicro; // Processing start time
uint32_t cMicro; // current time
bool rState = 1; // Infrared receiver module status 0: LOW, 1: HIGH
// Get current time in milliseconds
sMilli = millis();
// Infinite loop until specific condition (signal received or 30 seconds elapsed)
while(1) {
// Get start time to wait for infrared reception
wMilli = millis();
// Waiting to receive an inverted signal
while (digitalRead(IR_R_PIN) == rState) {
// After waiting for 0.5 seconds or more
if (millis() - wMilli > 500) {
// When the count exceeds 10 times
if ( irCount > 10 ) {
Serial.println("");
Serial.print(irCount);
Serial.println(":recvOK!");
delay(1);
// Save button name to EEPROM and remote control data to file
//xTaskCreatePinnedToCore(saveIr,"saveIr", 4096, ((irCount - 1), request), 1, NULL, 1); //Task1実行
if (saveIr( (irCount - 1), request)) {
// Returned OK from the web server because it ended normally
request->send(200, "text", "OK");
Serial.println("setRemocon OK");
} else {
// Reply NG due to failure to save signal
request->send(200, "text", "NG");
Serial.println("setRemocon NG");
}
return; // completed successfully
}
// If there are not more than 10 0,1 signals, receive again from zero due to noise
irCount = 0;
}
// Timeout after 15 seconds of processing
if ( millis() - sMilli > 15000 ) {
request->send(200, "text", "NG");// Send NG due to timeout
Serial.println("setRemocon T.O.");
return; // Processing ends after 15 seconds (no reception)
}
}
// Get the current time or elapsed time at the start of signal reception
if ( irCount == 0 ) {
sMicro = micros();
lastt = 0;
irCount++;
Serial.print("ir:");
// Processing after starting signal reception processing (irCount is 1 or more)
} else {
//Calculate the elapsed time from the time when the state change of the infrared receiver last changed
cMicro = micros();
deltt = ( (cMicro - sMicro)/ 10 ) - lastt;
irData[(irCount - 1 )] = deltt;
// Save last changed elapsed time for next elapsed time calculation
lastt = lastt + deltt;
irCount++;
Serial.print( deltt );
Serial.print(",");
}
// Change the value that detects the state change in the next While
rState = !rState;
}
}
// Save button name to EEPROM and remote control data to file
bool saveIr(unsigned short irLength, AsyncWebServerRequest *request){
String setirname = "";
String setNumStr = "";
// Get and check button number (HTTP GET request parameter)
if (request->hasParam("n")) {
setNumStr = request->getParam("n")->value();
} else {
return false;
}
// Get and check button name (parameter of HTTP GET request)
if (request->hasParam("a")) {
setirname = request->getParam("a")->value();
} else {
return false;
}
// Convert the button number from String type to int type
int setNum = setNumStr.toInt();
// Append the identification character "O:" to the beginning of the button name
setirname = "O:" + setirname;
// Define a variable with matching type for storage in EEPROM
st_remocon remRom;
// Convert from String to char type (Length +1 to add end character)
setirname.toCharArray(remRom.remo_name, setirname.length()+1);
// Calculate memory location and write to EEPROM
int memPos = (65 * setNum);
EEPROM.put<st_remocon>(memPos, remRom);
EEPROM.commit();
Serial.println("setIr:" + String(setNum) + ":" + setirname);
// Create a file name to save the remote control signal (the file name is the button number)
String t_file = "/" + setNumStr;
Serial.println( "recvFile:" + t_file);
// open file in write mode
File fw = SPIFFS.open(t_file.c_str(), "w");
// Write remote control signal length first (first line)
fw.println( String( irLength, HEX ) );
// Write the time length of 0 and 1 of the remote control signal (from the second line)
for (int i = 0; i < irLength; i++) {
fw.println( String( irData[i], HEX ) );
}
// Close the file when writing is complete
fw.close();
// Returns true because processing was completed normally
return true;
}
// Infrared transmission process
bool contRemocon (String setNumStr) { // save transmission number
// Variable declaration
unsigned short irCount = 0; // Number of HIGH and LOW signals
unsigned long l_now = 0; // number of HIGH and LOW signals
unsigned long sndt = 0; // Elapsed time since transmission started
// Get remote control signal from file (file name is button number)
String t_file = "/" + setNumStr;
Serial.println( "sendFile:" + t_file);
// open file
File fr = SPIFFS.open(t_file.c_str(), "r");
// File open failure
if(!fr || fr.isDirectory()){
Serial.println("FileOpen NG");
return false;
}
// Get readout signal length (number of 0 and 1) only for the first row
String snum = fr.readStringUntil('\n');
// Convert the string in the first line to numeric type
irCount = strtol(snum.c_str(), NULL, 16);
Serial.println( "sendData:" + String(irCount));
// Once read from the file to the variable irData and saved
for (int i = 0; i < irCount; i++) {
snum = fr.readStringUntil('\n');
irData[i] = strtol(snum.c_str(), NULL, 16);
//Serial.print("," + String(irData[i]));
}
fr.close();
// get transmission start time
l_now = micros();
// Loop for the number of signals 0 and 1 with a For statement
for (int i = 0; i < irCount; i++) {
// Calculate signal end time from transmission start
sndt += irData[i];
do {
// If i is an even number, the infrared is ON, if it is an odd number, it remains OFF
// Transmit with ON time at carrier frequency 38kHz (approximately half of 26μSec period)
digitalWrite(IR_S_PIN, !(i&1));
microWait(13);
// Transmits at carrier frequency 38kHz (half of 26μSec cycle) during OFF time
digitalWrite(IR_S_PIN, 0);
microWait(13);
// Loop from transmission start until signal end time exceeds
} while (long(l_now + (sndt * 10) - micros()) > 0);
}
// Returned true because it ended normally
Serial.println(":End");
return true;
}
// wait in microseconds
void microWait(signed long waitTime) {
unsigned long waitStartMicros = micros();
while (micros() - waitStartMicros < waitTime) {};
}
// WebServer setting processing
void setup_webserver() {
// WebServer processing settings (send control screen)
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println ( "Top" );
request->send(SPIFFS, "/top.html", "text/html");
});
// For favicon display (optional)
webServer.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println ( "favicon" );
request->send(SPIFFS, "/favicon.ico", "image/x-icon");
});
// WebServerLED control (LED control)
webServer.on("/led", HTTP_GET, [](AsyncWebServerRequest *request){
String setLedVal = request->getParam("o")->value();
if (setLedVal == "ON") {
digitalWrite ( LED_PIN, true );
Serial.println ( "led on" );
} else {
digitalWrite ( LED_PIN, false );
Serial.println ( "led off" );
}
request->send(200, "text/plain", "OK");
});
// WebServer processing settings (sending Javascript for control screen)
webServer.on("/rem.js", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println ( "js" );
request->send(SPIFFS, "/rem.js", "text/html");
});
// WebServer processing settings (send setting screen)
webServer.on("/set", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println ( "set" );
request->send(SPIFFS, "/set.html", "text/html");
});
// WebServer processing settings (button information transmission)
webServer.on("/getrem", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println ( "getrem" );
getRemocon(request);
});
// WebServer processing settings (remote control reception settings)
webServer.on("/setrem", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println ( "setrem" );
setRemocon(request);
});
// WebServer processing settings (remote control transmission)
webServer.on("/cntrem", HTTP_GET, [](AsyncWebServerRequest *request){
Serial.println ( "cntrem" );
// Get and check button number (HTTP GET request parameter) and send process
if (request->hasParam("n") && contRemocon( request->getParam("n")->value() )) {
request->send(200, "text", "OK");
Serial.println("Web send OK");
} else {
request->send(200, "text", "NG");
Serial.println("Web send NG");
}
});
// WebServer start processing
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
webServer.begin();
Serial.println ( "Web server started" );
}
// Button information transmission function
void getRemocon(AsyncWebServerRequest *request) {
// Create transmission data (JSON format)
String senddata = "{";
// Declare a variable to store EEPROM data
st_remocon remRom;
// Read 10 pieces of button information and reply
for (byte i = 0; i < 10; i++) {
// Calculate EEPROM memory location
int memPos = (65 * i);
// Erase so that the previous value 'O:' does not remain
remRom.remo_name[0] = 'n';
// Get data from EEPROM
EEPROM.get<st_remocon>(memPos, remRom);
// Check if data is saved
if (remRom.remo_name[0] == 'O' && remRom.remo_name[1] == ':' ) {
// If the response string length exceeds 1, add "," (delimiter from the second and subsequent characters)
if (senddata.length() > 1) {
senddata += ",";
}
// Replace the returned value with String type once (to remove "O:")
String getirname = String(remRom.remo_name);
// Create reply string (from 2 to the end to remove "O:")
senddata += "\"" + (String)i + "\":\"" + getirname.substring(2,getirname.length()) + "\"";
}
}
// Add "}" at the end to close the JSON data
senddata += "}";
// Send the created response (JSON) data from the web server
request->send(200, "text", senddata);
Serial.println( "getRemocon:" + senddata);
}
The HTML and Javascript files are in the Zip file, but only “outdoor.html” is displayed here.
<!doctype html>
<!-- ◆◆◆HTML Tag◆◆◆ -->
<html>
<!-- ◆◆◆head Tag◆◆◆ -->
<head>
<meta charset='UTF-8'/>
<meta name='viewport' content='width=device-width'/>
<!-- ##### StyleSheet ##### -->
<style type='text/css'><!--
#contents { width: 100%; max-width: 320px; }
#menu{ color: #fff; background: #222; }
.underTheEarthKai {
background-image: radial-gradient(50% 150%, #CCCCCC 5%, #777777 100%);
}
button { width:155px; height:35px }
#dispStatus{ color: #f00; }
footer { text-align: right; }
--></style>
<!-- ##### Javascript ##### -->
<script type='text/javascript'>
// ############## Beebotte setting ############
var beToken = "#### TOKEN ####"; // Beebotte Token
var beChannel = "### CHANNEL ###"; // Beebotte Channel
// ############################################
// ● Remote control signal processing
var irFlg = false;
function snd(setNum) {
// ● Judgment during processing
if (irFlg) {
// ●If processing is in progress, display processing and exit.
document.getElementById('dispStatus').innerHTML = "<b>Processing</b>";
return;
}
// ● Set the action flag as being processed, and perform display processing during reception
irFlg=true;
document.getElementById('dispStatus').innerHTML = "<b>Sending remote control</b>";
var xhr = new XMLHttpRequest();
// ● Make send data
var sdata = "{ \"data\":" + setNum + "}";
// ● Create an access URL
var url = "https://api.beebotte.com/v1/data/publish/" + beChannel + "/resource1?token=" + beToken;
xhr.timeout = 5000;
xhr.ontimeout = function(e){
irFlg=false;
// ● Show failure in status
document.getElementById('dispStatus').innerHTML = "<b>Access Timeout Failure!</b>";
};
xhr.open("POST", url);
xhr.setRequestHeader( 'Content-Type', 'application/json' );
xhr.send(sdata);
xhr.addEventListener("load",function(ev){
var resStr = xhr.responseText;
// ●When OK is received, the status is displayed in the if statement. Otherwise, display the state inside else
if ( resStr.indexOf("true") != -1 ) {
document.getElementById('dispStatus').innerHTML = "<b>Transmission Completed!</b>";
} else {
document.getElementById('dispStatus').innerHTML = "<b>Transmission Failure!</b>";
}
// ● Return the processing flag
irFlg=false;
});
}
</script>
</head>
<!-- ◆◆◆Body Tag◆◆◆ -->
<body class='underTheEarthKai'><center><div id='contents'>
<header><h3>Smart Remote controller [OutDoor]</h3></header>
<div id='menu'>Controller Screen</div>
<br/>
<!-- ##### Button Tag ##### -->
<table>
<tr>
<td><button id='btn0' class='cntbtn' onClick="snd(0)">
<font size=+1><span id='spn0'>1</span></font></button></td>
<td><button id='btn1' class='cntbtn' onClick="snd(1)">
<font size=+1><span id='spn1'>2</span></font></button></td>
</tr>
<tr>
<td><button id='btn2' class='cntbtn' onClick="snd(2)">
<font size=+1><span id='spn2'>3</span></font></button></td>
<td><button id='btn3' class='cntbtn' onClick="snd(3)">
<font size=+1><span id='spn3'>4</span></font></button></td>
</tr>
<tr>
<td><button id='btn4' class='cntbtn' onClick="snd(4)">
<font size=+1><span id='spn4'>5</span></font></button></td>
<td><button id='btn5' class='cntbtn' onClick="snd(5)">
<font size=+1><span id='spn5'>6</span></font></button></td>
</tr>
<tr>
<td><button id='btn6' class='cntbtn' onClick="snd(6)">
<font size=+1><span id='spn6'>7</span></font></button></td>
<td><button id='btn7' class='cntbtn' onClick="snd(7)">
<font size=+1><span id='spn7'>8</span></font></button></td>
</tr>
<tr>
<td><button id='btn8' class='cntbtn' onClick="snd(8)">
<font size=+1><span id='spn8'>9</span></font></button></td>
<td><button id='btn9' class='cntbtn' onClick="snd(9)">
<font size=+1><span id='spn9'>10</span></font></button></td>
</tr>
</table>
<!-- ##### DivTag(Display Status) ##### -->
<div id='dispStatus'><br></div>
<!-- ##### Footer Tag ##### -->
<footer><font size=-1>©Hobby-IT</font></footer>
</div></center></body>
</html>
Comments