B4R Question Porting ESP8266 IR Remote Control Project to B4R

bdunkleysmith

Active Member
Licensed User
Longtime User
I have been working on an IR remote control project to assist my elderly father control his cable TV box and TV itself. I have a version working using a B4A kiosk mode app on a tablet connected via Bluetooth to an Arduino UNO which drives two IR emitters; one for the cable TV box and one for the TV.

But this Talk to your CD player using Google Home project piqued my interest and I began duplicating it in B4R.

I've created B4R code which connects the ESP8266 to my network and can drive an IR emitter to control the TV, but I'm struggling to replicate the functionality of the original Arduino code:

B4X:
#include <Arduino.h>
#include <IRremoteESP8266.h>
#include <IRsend.h>
#include <framework.h>

#define PanasonicAddress      (0x4004)

typedef struct {
  char const * name;
  uint32_t code;
} button_t;

button_t buttons[] = {
  { "power", 0x0538BC81 },
  { "pause", 0x05505005 },
  { "play", 0x05505005 },
  { "next song", 0x0538526F },
  { "previous song", 0x053892AF },
  { "track 1", 0x05380835 },
  { "track 2", 0x053888B5 },
  { "track 3", 0x05384875 },
  { "track 4", 0x0538C8F5 },
  { "track 5", 0x05382815 },
  { "track 6", 0x0538A895 },
  { "track 7", 0x05386855 },
  { "track 8", 0x0538E8D5 },
  { "track 9", 0x05381825 },
  { "two digits", 0x0538211C },
  { "volume up", 0x05000401 },
  { "volume down", 0x05008481 },
  { NULL, 0x0 }
};

IRsend irsend(4);  // An IR LED is controlled by GPIO pin 4 (D2)

void handleRoot(void)
{
    String message = "";

    if (Framework::server.args() >= 1 &&
        Framework::server.argName(0) == "req") {
         
      String buttonReq = Framework::server.arg(0);    
      button_t const *p;
      for (p = buttons; p->name; p++) {
        if ( String(p->name) == buttonReq ) {
          break;
        }
      }
      if (p->name) {
          message += "Sending ";
          message += p->name;
          message += "\n\n";
          irsend.sendPanasonic(PanasonicAddress, p->code);
      } else {
          message += "'";
          message += buttonReq;
          message +="' not found\n\n";
      }
    }
    message += "Valid commands are:<ul>\n\n";
    for (button_t const *p = buttons; p->name; p++) {
      message += "<li>";
      message += "<a href=\"/ir?ir=";
      message += p->name;
      message += "\">";
      message += p->name;
      message += "</li>\n";
    }
    message += "</ul>";

    Serial.println(message);
    Framework::server.send( 200, "text/html", message );
}


void setup()
{
  Framework::begin();
  Framework::server.on("/ir", handleRoot);
  Framework::server.begin();
  delay(500);  // Wait a bit for the serial connection to be establised.
  irsend.begin();

 if (!MDNS.begin("ir-interface")) {
    Serial.println("Error setting up MDNS responder!");
    while(1) {
      delay(1000);
    }
  }
  MDNS.addService("http", "tcp", 80);
 
  Serial.println("Ready");
}

void loop() {
  // test only
  irsend.sendPanasonic(PanasonicAddress, 0x05505005);

  Framework::handle();
}

particularly where it appears to set the endpoint to "ir-interface" (!MDNS.begin("ir-interface"))
in lieu of the 192.168.0.15 assigned to the ESP8266 and handles calls to the endpoint (Framework::server.on("/ir", handleRoot).

Below is my current code which can extract a code to trigger sending of the respective IR code via a web browser, eg. 192.168.0.15/?req=power:

B4X:
Sub Process_Globals
    Private wifi As ESP8266WiFi
    Private server As WiFiServerSocket
    Private sp As Serial
    Private astream As AsyncStreams
    Private bc As ByteConverter
    Private irsend As IrSend
End Sub

Private Sub AppStart
    sp.Initialize(115200)
    Log("AppStart")
    ScanNetworks
    ConnectToNetwork
    irsend.Enable(4, 38) 'frequency = 38khz
    server.Initialize(80, "server_NewConnection")
    server.Listen
End Sub

Sub Astream_NewData (Buffer() As Byte)
    If bc.IndexOf(Buffer, "GET") <> -1 Then
        Dim command As String
        Dim i1 As Int = 0
        Dim i2 As Int = 0
        For Each b1() As Byte In bc.Split(Buffer, " ")
            If i1 = 1 Then
                For Each b2() As Byte In bc.Split(b1, "=")
                    If i2 = 1 Then
                        command = bc.StringFromBytes(b2)
                        sendNewData(command)                      
                    End If
                    i2 = i2 + 1
                Next
            End If
            i1 = i1 + 1
        Next
    Else
        CallSubPlus("CloseConnection", 200, 0)
    End If
End Sub

Private Sub CloseConnection(u As Byte)
    Log("close connection")
    If server.Socket.Connected Then
        server.Socket.Stream.Flush
        server.Socket.Close
    End If
End Sub

Sub AStream_Error
    Log("error")
End Sub

Sub Server_NewConnection (NewSocket As WiFiSocket)
    Log("Client connected")
    astream.Initialize(NewSocket.Stream, "Astream_NewData", "Astream_Error")
End Sub

Sub sendNewData(command As String)
    Select command
    Case "power"
        irsend.sendPanasonic(0x4004, 0x100BCBD)    'Toggle TV power on/off
        Log("Sending IR code for: ", command)
    End Select
End Sub

Sub ScanNetworks
    Dim numberOfNetworks As Byte = wifi.Scan
    Log("Found ", numberOfNetworks, " networks")
    For c = 0 To numberOfNetworks - 1
        Log("SSID: ", wifi.ScannedSSID(c), "|RSSI: ", wifi.ScannedRSSI(c))
    Next
End Sub

Sub ConnectToNetwork
    If wifi.Connect2("SSID", "password") = False Then
        Log("Error connecting to network")
        Return
    Else
        Log("Connected to network")
        Log("My IP address: ", wifi.LocalIp)
    End If
End Sub

but I think it's pretty clunky and it works only once, with follow-up calls ignored, because there is an error thrown as reported in the log:

B4X:
Found 5 networks
SSID: Telstra1943|RSSI: -30
SSID: EQ|RSSI: -91
SSID: DIRECT-E6-HP OfficeJet Pro 6970|RSSI: -50
SSID: Fon WiFi|RSSI: -89
SSID: Telstra0C3E|RSSI: -93
Connected to network
My IP address: 192.168.0.15
Client connected
Sending IR code for: power
close connection
error
close connection
close connection
close connection

Any pointers or suggestions on how I should use better or other B4R code to achieve the functionality of the original Arduino project code or at least address the error which prevents follow-up calls, would be appreciated.
 
Last edited:

bdunkleysmith

Active Member
Licensed User
Longtime User
Thanks for your usual great guidance Erel.

Apart from the problems cited above, I decided to investigate alternatives because I balked at the requirement to install a reverse-proxy and TLS-certificates on my router.

However I've struck another issue with my chosen alternative method involving use of MQTT which I have documented here.
 
Upvote 0
Top