In the Video
Japanese【日本語】
English【英語】
1. Overview
We will make a home appliance remote control that can be operated from a smartphone.
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) | – | 〇 | This Post |
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. | – | 〇 | Another 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.)
//*************************************************************************
// Home Remocon Ver2023.1.27
// 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 "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
// 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 );
}
// After setup is complete, repeat processing until power is turned off
void loop(void){
}
// 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
// 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);
}
Comments