diff --git a/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoControlCommand.java b/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoControlCommand.java deleted file mode 100644 index 374cc8b..0000000 --- a/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoControlCommand.java +++ /dev/null @@ -1,51 +0,0 @@ -package net.irext.ircontrol.controller; - -import com.google.gson.Gson; -import net.irext.decode.sdk.bean.ACStatus; -import net.irext.ircontrol.controller.base.ControlCommand; -import org.jspecify.annotations.NonNull; - -import java.util.Base64; - -/** - * Filename: ControlCommand.java - * Revised: Date: 2026-01-22 - * Revision: Revision: 1.0 - *

- * Description: Remote command to Arduino remote - *

- * Revision log: - * 2026-01-22: created by strawmanbobi - */ -public class ArduinoControlCommand extends ControlCommand { - - public ArduinoControlCommand(int keyCode, ACStatus acStatus) { - this.keyCode = keyCode; - this.acStatus = acStatus; - } - - public ArduinoControlCommand() { - } - - public int getKeyCode() { - return keyCode; - } - - public void setKeyCode(int keyCode) { - this.keyCode = keyCode; - } - - public ACStatus getAcStatus() { - return acStatus; - } - - public void setAcStatus(ACStatus acStatus) { - this.acStatus = acStatus; - } - - @Override - public @NonNull String toString() { - String jsonStr = new Gson().toJson(this); - return Base64.getEncoder().encodeToString(jsonStr.getBytes()); - } -} diff --git a/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoRemote.java b/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoRemote.java index 0b14b91..1fcafcd 100644 --- a/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoRemote.java +++ b/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoRemote.java @@ -2,35 +2,218 @@ package net.irext.ircontrol.controller; import android.content.Context; import android.util.Log; +import com.google.gson.Gson; import net.irext.decode.sdk.bean.ACStatus; -import net.irext.ircontrol.controller.base.IRemote; +import net.irext.ircontrol.controller.base.ControlCommand; +import net.irext.ircontrol.controller.base.ControlHelper; +import net.irext.ircontrol.controller.base.Remote; +import org.jspecify.annotations.NonNull; -import static net.irext.ircontrol.controller.ArduinoSocket.*; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.Socket; +import java.util.Base64; /** * Filename: ArduinoRemote.java * Revised: Date: 2026-01-18 * Revision: Revision: 1.0 *

- * Description: Remote implementation by Arduino + * Description: Communication interface to Arduino *

* Revision log: *2026-01-18: created by strawmanbobi */ -public class ArduinoRemote implements IRemote { - +public class ArduinoRemote extends Remote { private static final String TAG = ArduinoRemote.class.getSimpleName(); - Context mContext; - ArduinoSocket mArduinoSocket; + public static final int EMITTER_DISCONNECTED = 0; + public static final int EMITTER_CONNECTED = 1; + public static final int EMITTER_WORKING = 2; - public ArduinoRemote(Context context, ArduinoSocket socket) { - mContext = context; - mArduinoSocket = socket; + public static final int EMITTER_PORT = 8000; + + public static final String A_REQUEST_HELLO = "a_hello"; + public static final String E_RESPONSE_HELLO = "e_hello"; + + public static final String A_REQUEST_BIN = "a_bin"; + public static final String E_RESPONSE_BIN = "e_bin"; + + public static final String A_REQUEST_CTRL = "a_control"; + public static final String E_RESPONSE_CTRL = "e_control"; + + private Socket emitterConn = null; + private int connectionStatus = EMITTER_DISCONNECTED; + private IRSocketEmitterCallback callback; + + private Context mContext = null; + + private static ArduinoRemote mInstance; + + public static ArduinoRemote getInstance(Context context, IRSocketEmitterCallback callback) { + if (mInstance == null) { + mInstance = new ArduinoRemote(context, callback); + } + return mInstance; } - @Override - public int irControl(int category, int subCategory, int keyCode) { + public Context getContext() { + return mContext; + } + + public void setContext(Context mContext) { + this.mContext = mContext; + } + + public interface IRSocketEmitterCallback { + void onConnected(); + void onDisconnected(); + void onResponse(String response); + } + + public ArduinoRemote(Context context, IRSocketEmitterCallback callback) { + this.mContext = context; + this.callback = callback; + } + + public void setCallback(IRSocketEmitterCallback callback) { + this.callback = callback; + } + + public int getConnectionStatus() { + return connectionStatus; + } + + public void connectToEmitter(String ipAddress, String port) { + if (connectionStatus == EMITTER_DISCONNECTED) { + if (ipAddress == null || port == null) { + return; + } + new Thread(() -> { + try { + emitterConn = new Socket(ipAddress, Integer.parseInt(port)); + emitterConn.setKeepAlive(true); + connectionStatus = EMITTER_CONNECTED; + + onConnected(); + + BufferedReader in = new BufferedReader(new InputStreamReader(emitterConn.getInputStream())); + String response; + while ((response = in.readLine()) != null) { + onResponse(response); + } + + if (callback != null) { + callback.onDisconnected(); + } + + connectionStatus = EMITTER_DISCONNECTED; + } catch (IOException ioException) { + Log.e(TAG, "Connection error: " + ioException.getMessage()); + + if (callback != null) { + callback.onDisconnected(); + } + + connectionStatus = EMITTER_DISCONNECTED; + } + }).start(); + } else { + disconnect(); + } + } + + public void disconnect() { + try { + if (emitterConn != null && !emitterConn.isClosed()) { + emitterConn.close(); + } + connectionStatus = EMITTER_DISCONNECTED; + } catch (IOException e) { + Log.e(TAG, "Error closing connection: " + e.getMessage()); + } + } + + public void sendHelloToEmitter() { + new Thread(() -> { + try { + Log.d(TAG, "sending a_hello to emitter"); + PrintWriter out = new PrintWriter(emitterConn.getOutputStream(), true); + out.println(A_REQUEST_HELLO); + } catch (IOException e) { + Log.e(TAG, "Error sending hello: " + e.getMessage()); + } + }).start(); + } + + public void sendBinToEmitter(byte[] binContent, int categoryId, int subCate) { + if (binContent == null) { + Log.e(TAG, "binary bytes is null"); + return; + } + String binBase64 = Base64.getEncoder().encodeToString(binContent); + String binStr = A_REQUEST_BIN + "," + categoryId + "," + subCate + "," + binBase64.length() + "," + binBase64; + Log.d(TAG, "sending bin in base64: " + binStr); + new Thread(() -> { + try { + PrintWriter out = new PrintWriter(emitterConn.getOutputStream(), true); + out.println(binStr); + } catch (Exception e) { + Log.e(TAG, "Error sending binary data: " + e.getMessage()); + } + }).start(); + } + + public void sendControlToEmitter(String command) { + + String commandStr = A_REQUEST_CTRL + "," + command.length() + "," + command; + + Log.d(TAG, "sending command in base64: " + commandStr); + new Thread(() -> { + try { + PrintWriter out = new PrintWriter(emitterConn.getOutputStream(), true); + out.println(commandStr); + } catch (IOException e) { + Log.e(TAG, "Error sending control data: " + e.getMessage()); + } + }).start(); + } + + public void sendDecodedToEmitter(String binContent) { + new Thread(() -> { + try { + PrintWriter out = new PrintWriter(emitterConn.getOutputStream(), true); + out.println(binContent); + } catch (IOException e) { + Log.e(TAG, "Error sending decoded data: " + e.getMessage()); + } + }).start(); + } + + private void onConnected() { + if (callback != null) { + Log.d(TAG, "the emitter is connected"); + callback.onConnected(); + } + } + + private void onResponse(String response) { + if (response.startsWith(ArduinoRemote.E_RESPONSE_HELLO)) { + Log.d(TAG, "received e_hello"); + } else if (response.startsWith(ArduinoRemote.E_RESPONSE_BIN)) { + Log.d(TAG, "received e_bin"); + } else if (response.startsWith(ArduinoRemote.E_RESPONSE_CTRL)) { + connectionStatus = EMITTER_WORKING; + } else { + Log.e(TAG, "unexpected response : " + response); + } + callback.onResponse(response); + } + + + public void irControl(int category, int subCategory, int keyCode) { Log.d(TAG, "irControl, category = " + category + ", subCategory = " + subCategory + ", keyCode = " + keyCode); @@ -40,8 +223,40 @@ public class ArduinoRemote implements IRemote { ArduinoControlCommand command = new ArduinoControlCommand(inputKeyCode, acStatus); String controlCommand = command.toString(); - mArduinoSocket.sendControlToEmitter(controlCommand); + sendControlToEmitter(controlCommand); - return 0; } -} + + private static class ArduinoControlCommand extends ControlCommand { + + public ArduinoControlCommand(int keyCode, ACStatus acStatus) { + this.keyCode = keyCode; + this.acStatus = acStatus; + } + + public ArduinoControlCommand() { + } + + public int getKeyCode() { + return keyCode; + } + + public void setKeyCode(int keyCode) { + this.keyCode = keyCode; + } + + public ACStatus getAcStatus() { + return acStatus; + } + + public void setAcStatus(ACStatus acStatus) { + this.acStatus = acStatus; + } + + @Override + public @NonNull String toString() { + String jsonStr = new Gson().toJson(this); + return Base64.getEncoder().encodeToString(jsonStr.getBytes()); + } + } +} \ No newline at end of file diff --git a/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoSocket.java b/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoSocket.java deleted file mode 100644 index a461577..0000000 --- a/android-example/app/src/main/java/net/irext/ircontrol/controller/ArduinoSocket.java +++ /dev/null @@ -1,188 +0,0 @@ -package net.irext.ircontrol.controller; - -import android.util.Log; -import android.util.Patterns; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.net.Socket; -import java.util.Base64; - -/** - * Filename: ArduinoSocket.java - * Revised: Date: 2026-01-18 - * Revision: Revision: 1.0 - *

- * Description: Communication interface to Arduino - *

- * Revision log: - *2026-01-18: created by strawmanbobi - */ -public class ArduinoSocket { - private static final String TAG = ArduinoSocket.class.getSimpleName(); - - public static final int EMITTER_DISCONNECTED = 0; - public static final int EMITTER_CONNECTED = 1; - public static final int EMITTER_WORKING = 2; - - public static final int EMITTER_PORT = 8000; - - public static final String A_REQUEST_HELLO = "a_hello"; - public static final String E_RESPONSE_HELLO = "e_hello"; - - public static final String A_REQUEST_BIN = "a_bin"; - public static final String E_RESPONSE_BIN = "e_bin"; - - public static final String A_REQUEST_CTRL = "a_control"; - public static final String E_RESPONSE_CTRL = "e_control"; - - private Socket emitterConn = null; - private int connectionStatus = EMITTER_DISCONNECTED; - private IRSocketEmitterCallback callback; - - public interface IRSocketEmitterCallback { - void onConnected(); - void onDisconnected(); - void onResponse(String response); - } - - public ArduinoSocket(IRSocketEmitterCallback callback) { - this.callback = callback; - } - - public void setCallback(IRSocketEmitterCallback callback) { - this.callback = callback; - } - - public int getConnectionStatus() { - return connectionStatus; - } - - public void connectToEmitter(String ipAddress, String port) { - if (connectionStatus == EMITTER_DISCONNECTED) { - if (ipAddress == null || port == null) { - return; - } - new Thread(() -> { - try { - emitterConn = new Socket(ipAddress, Integer.parseInt(port)); - emitterConn.setKeepAlive(true); - connectionStatus = EMITTER_CONNECTED; - - onConnected(); - - BufferedReader in = new BufferedReader(new InputStreamReader(emitterConn.getInputStream())); - String response; - while ((response = in.readLine()) != null) { - onResponse(response); - } - - if (callback != null) { - callback.onDisconnected(); - } - - connectionStatus = EMITTER_DISCONNECTED; - } catch (IOException ioException) { - Log.e(TAG, "Connection error: " + ioException.getMessage()); - - if (callback != null) { - callback.onDisconnected(); - } - - connectionStatus = EMITTER_DISCONNECTED; - } - }).start(); - } else { - disconnect(); - } - } - - public void disconnect() { - try { - if (emitterConn != null && !emitterConn.isClosed()) { - emitterConn.close(); - } - connectionStatus = EMITTER_DISCONNECTED; - } catch (IOException e) { - Log.e(TAG, "Error closing connection: " + e.getMessage()); - } - } - - public void sendHelloToEmitter() { - new Thread(() -> { - try { - Log.d(TAG, "sending a_hello to emitter"); - PrintWriter out = new PrintWriter(emitterConn.getOutputStream(), true); - out.println(A_REQUEST_HELLO); - } catch (IOException e) { - Log.e(TAG, "Error sending hello: " + e.getMessage()); - } - }).start(); - } - - public void sendBinToEmitter(byte[] binContent, int categoryId, int subCate) { - if (binContent == null) { - Log.e(TAG, "binary bytes is null"); - return; - } - - String binBase64 = Base64.getEncoder().encodeToString(binContent); - String binStr = A_REQUEST_BIN + "," + categoryId + "," + subCate + "," + binBase64.length() + "," + binBase64; - Log.d(TAG, "sending bin in base64: " + binStr); - new Thread(() -> { - try { - PrintWriter out = new PrintWriter(emitterConn.getOutputStream(), true); - out.println(binStr); - } catch (Exception e) { - Log.e(TAG, "Error sending binary data: " + e.getMessage()); - } - }).start(); - } - - public void sendControlToEmitter(String command) { - String commandStr = A_REQUEST_CTRL + "," + command.length() + "," + command; - Log.d(TAG, "sending command in base64: " + commandStr); - new Thread(() -> { - try { - PrintWriter out = new PrintWriter(emitterConn.getOutputStream(), true); - out.println(commandStr); - } catch (IOException e) { - Log.e(TAG, "Error sending control data: " + e.getMessage()); - } - }); - } - - public void sendDecodedToEmitter(String binContent) { - new Thread(() -> { - try { - PrintWriter out = new PrintWriter(emitterConn.getOutputStream(), true); - out.println(binContent); - } catch (IOException e) { - Log.e(TAG, "Error sending decoded data: " + e.getMessage()); - } - }).start(); - } - - - private void onConnected() { - if (callback != null) { - Log.d(TAG, "the emitter is connected"); - callback.onConnected(); - } - } - - private void onResponse(String response) { - if (response.startsWith(ArduinoSocket.E_RESPONSE_HELLO)) { - ; - } else if (response.startsWith(ArduinoSocket.E_RESPONSE_BIN)) { - ; - } else if (response.startsWith(ArduinoSocket.E_RESPONSE_CTRL)) { - connectionStatus = EMITTER_WORKING; - } else { - Log.e(TAG, "unexpected response : " + response); - } - callback.onResponse(response); - } -} \ No newline at end of file diff --git a/android-example/app/src/main/java/net/irext/ircontrol/controller/PhoneRemote.java b/android-example/app/src/main/java/net/irext/ircontrol/controller/PhoneRemote.java index fc76ef0..e0f105f 100644 --- a/android-example/app/src/main/java/net/irext/ircontrol/controller/PhoneRemote.java +++ b/android-example/app/src/main/java/net/irext/ircontrol/controller/PhoneRemote.java @@ -4,7 +4,8 @@ import android.content.Context; import android.util.Log; import net.irext.decode.sdk.IRDecode; import net.irext.decode.sdk.bean.ACStatus; -import net.irext.ircontrol.controller.base.IRemote; +import net.irext.ircontrol.controller.base.ControlHelper; +import net.irext.ircontrol.controller.base.Remote; /** * Filename: PhoneRemote.java @@ -16,19 +17,27 @@ import net.irext.ircontrol.controller.base.IRemote; * Revision log: *2026-01-18: created by strawmanbobi */ -public class PhoneRemote implements IRemote { +public class PhoneRemote extends Remote { private static final String TAG = PhoneRemote.class.getSimpleName(); - IRDecode mIRDecode; + private Context mContext; + private IRDecode mIRDecode; + + private static PhoneRemote mInstance; - Context mContext; public PhoneRemote(Context context) { mContext = context; mIRDecode = new IRDecode(); } - @Override + public static PhoneRemote getInstance(Context context) { + if (mInstance == null) { + mInstance = new PhoneRemote(context); + } + return mInstance; + } + public int irControl(int category, int subCategory, int keyCode) { int []decoded; StringBuilder debugStr = new StringBuilder(); diff --git a/android-example/app/src/main/java/net/irext/ircontrol/controller/ControlHelper.java b/android-example/app/src/main/java/net/irext/ircontrol/controller/base/ControlHelper.java similarity index 97% rename from android-example/app/src/main/java/net/irext/ircontrol/controller/ControlHelper.java rename to android-example/app/src/main/java/net/irext/ircontrol/controller/base/ControlHelper.java index 8e084d6..62737d4 100644 --- a/android-example/app/src/main/java/net/irext/ircontrol/controller/ControlHelper.java +++ b/android-example/app/src/main/java/net/irext/ircontrol/controller/base/ControlHelper.java @@ -1,4 +1,4 @@ -package net.irext.ircontrol.controller; +package net.irext.ircontrol.controller.base; import android.content.Context; import android.hardware.ConsumerIrManager; @@ -9,7 +9,7 @@ import net.irext.ircontrol.utils.ToastUtils; import java.util.Objects; -import static net.irext.ircontrol.controller.base.IRemote.*; +import static net.irext.ircontrol.controller.base.Remote.*; /** * Filename: ControlHelper.java diff --git a/android-example/app/src/main/java/net/irext/ircontrol/controller/base/IRemote.java b/android-example/app/src/main/java/net/irext/ircontrol/controller/base/Remote.java similarity index 77% rename from android-example/app/src/main/java/net/irext/ircontrol/controller/base/IRemote.java rename to android-example/app/src/main/java/net/irext/ircontrol/controller/base/Remote.java index a820535..0d73db3 100644 --- a/android-example/app/src/main/java/net/irext/ircontrol/controller/base/IRemote.java +++ b/android-example/app/src/main/java/net/irext/ircontrol/controller/base/Remote.java @@ -1,16 +1,16 @@ package net.irext.ircontrol.controller.base; /** - * Filename: IRemote.java + * Filename: Remote.java * Revised: Date: 2026-01-18 * Revision: Revision: 1.0 *

- * Description: IRemote interface + * Description: Remote interface *

* Revision log: *2026-01-18: created by strawmanbobi */ -public interface IRemote { +public abstract class Remote { public static final int KEY_POWER = 0; public static final int KEY_UP = 1; @@ -24,6 +24,8 @@ public interface IRemote { public static final int KEY_HOME = 9; public static final int KEY_MENU = 10; - int irControl(int category, int subCategory, int keyCode); + int irControl(int category, int subCategory, int keyCode) { + return 0; + } } diff --git a/android-example/app/src/main/java/net/irext/ircontrol/ui/fragment/ControlFragment.java b/android-example/app/src/main/java/net/irext/ircontrol/ui/fragment/ControlFragment.java index d6e4e83..12f036e 100644 --- a/android-example/app/src/main/java/net/irext/ircontrol/ui/fragment/ControlFragment.java +++ b/android-example/app/src/main/java/net/irext/ircontrol/ui/fragment/ControlFragment.java @@ -3,6 +3,7 @@ package net.irext.ircontrol.ui.fragment; import android.content.Context; import android.graphics.Color; import android.os.*; +import android.provider.ContactsContract; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -15,9 +16,8 @@ import net.irext.decode.sdk.IRDecode; import net.irext.ircontrol.R; import net.irext.ircontrol.bean.RemoteControl; import net.irext.ircontrol.controller.ArduinoRemote; -import net.irext.ircontrol.controller.ArduinoSocket; import net.irext.ircontrol.controller.PhoneRemote; -import net.irext.ircontrol.controller.base.IRemote; +import net.irext.ircontrol.controller.base.Remote; import net.irext.ircontrol.ui.activity.ControlActivity; import net.irext.ircontrol.utils.FileUtils; import net.irext.ircontrol.utils.MessageUtils; @@ -45,7 +45,8 @@ public class ControlFragment extends Fragment implements View.OnClickListener { private static final int CMD_GET_REMOTE_CONTROL = 0; - private ArduinoSocket mArduinoSocket; + private PhoneRemote mPhoneRemote; + private ArduinoRemote mArduinoRemote; private MsgHandler mHandler; @@ -96,12 +97,8 @@ public class ControlFragment extends Fragment implements View.OnClickListener { btnPlus.setOnClickListener(this); btnMinus.setOnClickListener(this); - mEtEmitterIp = view.findViewById(R.id.emitter_ip); - mBtnConnect = view.findViewById(R.id.btn_connect_emitter); - mVWConnectStatus = view.findViewById(R.id.vw_connect_status); - - // Initialize ArduinoSocket with callback - mArduinoSocket = new ArduinoSocket(new ArduinoSocket.IRSocketEmitterCallback() { + mPhoneRemote = PhoneRemote.getInstance(mParent); + mArduinoRemote = ArduinoRemote.getInstance(mParent, new ArduinoRemote.IRSocketEmitterCallback() { @Override public void onConnected() { onEmitterConnected(); @@ -118,6 +115,10 @@ public class ControlFragment extends Fragment implements View.OnClickListener { } }); + mEtEmitterIp = view.findViewById(R.id.emitter_ip); + mBtnConnect = view.findViewById(R.id.btn_connect_emitter); + mVWConnectStatus = view.findViewById(R.id.vw_connect_status); + mBtnConnect.setOnClickListener(new View.OnClickListener() { @Override @@ -129,7 +130,7 @@ public class ControlFragment extends Fragment implements View.OnClickListener { ToastUtils.showToast(mParent, mParent.getString(R.string.input_emitter_ip_address), null); return; } - mArduinoSocket.connectToEmitter(emitterIp, String.valueOf(ArduinoSocket.EMITTER_PORT)); + mArduinoRemote.connectToEmitter(emitterIp, String.valueOf(ArduinoRemote.EMITTER_PORT)); } }); @@ -152,13 +153,13 @@ public class ControlFragment extends Fragment implements View.OnClickListener { @Override public void onStop() { super.onStop(); - mArduinoSocket.disconnect(); + mArduinoRemote.disconnect(); } @Override public void onDestroyView() { super.onDestroyView(); - mArduinoSocket.disconnect(); + mArduinoRemote.disconnect(); } private void getRemote() { @@ -202,7 +203,7 @@ public class ControlFragment extends Fragment implements View.OnClickListener { } private void processEHello(String response) { - mArduinoSocket.sendHelloToEmitter(); + mArduinoRemote.sendHelloToEmitter(); } private void processEBin(String response) { @@ -210,7 +211,7 @@ public class ControlFragment extends Fragment implements View.OnClickListener { mCurrentRemoteControl.getRemoteMap() + FileUtils.FILE_NAME_EXT; byte []binContent = FileUtils.getByteArrayFromFile(binFileName); if (null != binContent) { - mArduinoSocket.sendBinToEmitter(binContent, mCurrentRemoteControl.getCategoryId(), mCurrentRemoteControl.getSubCategory()); + mArduinoRemote.sendBinToEmitter(binContent, mCurrentRemoteControl.getCategoryId(), mCurrentRemoteControl.getSubCategory()); } else { Log.e(TAG, "emitter sender could not open the binary file"); ToastUtils.showToast(mParent, mParent.getString(R.string.file_could_not_open), Toast.LENGTH_SHORT); @@ -222,11 +223,11 @@ public class ControlFragment extends Fragment implements View.OnClickListener { } private void onEmitterResponse(String response) { - if (response.startsWith(ArduinoSocket.E_RESPONSE_HELLO)) { + if (response.startsWith(ArduinoRemote.E_RESPONSE_HELLO)) { processEHello(response); - } else if (response.startsWith(ArduinoSocket.E_RESPONSE_BIN)) { + } else if (response.startsWith(ArduinoRemote.E_RESPONSE_BIN)) { processEBin(response); - } else if (response.startsWith(ArduinoSocket.E_RESPONSE_CTRL)) { + } else if (response.startsWith(ArduinoRemote.E_RESPONSE_CTRL)) { processECtrl(response); } else { Log.e(TAG, "unexpected response : " + response); @@ -237,39 +238,38 @@ public class ControlFragment extends Fragment implements View.OnClickListener { @Override public void onClick(View v) { vibrate(mParent); - IRemote remote = null; + Remote remote = null; int keyCode = 0; int id = v.getId(); if (id == R.id.iv_power) { - keyCode = IRemote.KEY_POWER; + keyCode = Remote.KEY_POWER; } else if (id == R.id.iv_up) { - keyCode = IRemote.KEY_UP; + keyCode = Remote.KEY_UP; } else if (id == R.id.iv_down) { - keyCode = IRemote.KEY_DOWN; + keyCode = Remote.KEY_DOWN; } else if (id == R.id.iv_left) { - keyCode = IRemote.KEY_LEFT; + keyCode = Remote.KEY_LEFT; } else if (id == R.id.iv_right) { - keyCode = IRemote.KEY_RIGHT; + keyCode = Remote.KEY_RIGHT; } else if (id == R.id.iv_ok) { - keyCode = IRemote.KEY_OK; + keyCode = Remote.KEY_OK; } else if (id == R.id.iv_plus) { - keyCode = IRemote.KEY_PLUS; + keyCode = Remote.KEY_PLUS; } else if (id == R.id.iv_minus) { - keyCode = IRemote.KEY_MINUS; + keyCode = Remote.KEY_MINUS; } else if (id == R.id.iv_back) { - keyCode = IRemote.KEY_BACK; + keyCode = Remote.KEY_BACK; } else if (id == R.id.iv_home) { - keyCode = IRemote.KEY_HOME; + keyCode = Remote.KEY_HOME; } else if (id == R.id.iv_menu) { - keyCode = IRemote.KEY_MENU; + keyCode = Remote.KEY_MENU; } - if (mArduinoSocket.getConnectionStatus() == ArduinoSocket.EMITTER_WORKING) { - remote = new ArduinoRemote(mParent, mArduinoSocket); + if (mArduinoRemote.getConnectionStatus() == ArduinoRemote.EMITTER_WORKING) { + mArduinoRemote.irControl(mCurrentRemoteControl.getCategoryId(), mCurrentRemoteControl.getSubCategory(), keyCode); } else { - remote = new PhoneRemote(mParent); + mPhoneRemote.irControl(mCurrentRemoteControl.getCategoryId(), mCurrentRemoteControl.getSubCategory(), keyCode); } - remote.irControl(mCurrentRemoteControl.getCategoryId(), mCurrentRemoteControl.getSubCategory(), keyCode); } private static class MsgHandler extends Handler { diff --git a/arduino-example/platformio.ini b/arduino-example/platformio.ini index eaa9f3a..510aa58 100644 --- a/arduino-example/platformio.ini +++ b/arduino-example/platformio.ini @@ -20,6 +20,9 @@ lib_deps = ArduinoGraphics Arduino_LED_Matrix adamvr/base64@^1.0.0 + ArduinoJson + IRremote build_flags = - -I ./src/ir_decode/include \ No newline at end of file + -I ./src/ir_decode/include + -DBOARD_ARDUINO \ No newline at end of file diff --git a/arduino-example/src/configure.h b/arduino-example/src/configure.h index 0d097e6..58f97a1 100644 --- a/arduino-example/src/configure.h +++ b/arduino-example/src/configure.h @@ -27,10 +27,12 @@ #include // Wi-Fi Configs -// #define SECRET_SSID "Maomao的小房子" -// #define SECRET_PASS "Maomao121207" -#define SECRET_SSID "maomao" -#define SECRET_PASS "20121207" +#define SECRET_SSID "Maomao的小房子" +#define SECRET_PASS "Maomao121207" +// #define SECRET_SSID "maomao" +// #define SECRET_PASS "20121207" + +// #define TEST_BIN_RECEIVE (1) // LED Matrix Definitions constexpr uint32_t chip[] = { diff --git a/arduino-example/src/control_command.cpp b/arduino-example/src/control_command.cpp new file mode 100644 index 0000000..0f4a39c --- /dev/null +++ b/arduino-example/src/control_command.cpp @@ -0,0 +1,66 @@ +/** +* + * Copyright (c) 2020-2025 IRext Opensource Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include + +#include "serial_log.h" +#include "control_command.h" + + +// public function definitions +int parseControlCommand(int category, const char *commandJson, + t_remote_ac_status *ac_status, int* keyCode) { + JsonDocument doc; + DeserializationError error = deserializeJson(doc, commandJson); + + if (error) { + serialPrint(LOG_ERROR, "Parsing command JSON failed: %s", error.c_str()); + return -1; + } + + // Parse AC status fields from JSON according to ACStatus structure + ac_status->ac_power = doc["acStatus"]["acPower"]; + ac_status->ac_temp = doc["acStatus"]["acTemp"]; + ac_status->ac_mode = doc["acStatus"]["acMode"]; + ac_status->ac_wind_dir = doc["acStatus"]["acWindDir"]; + ac_status->ac_wind_speed = doc["acStatus"]["acWindSpeed"]; + ac_status->ac_display = doc["acStatus"]["acDisplay"]; + ac_status->ac_sleep = doc["acStatus"]["acSleep"]; + ac_status->ac_timer = doc["acStatus"]["acTimer"]; + ac_status->change_wind_direction = doc["acStatus"]["changeWindDir"]; + *keyCode = doc["keyCode"]; + + serialPrint(LOG_VERBOSE, "--- AC Status ---"); + serialPrint(LOG_VERBOSE, "Power: %d", ac_status->ac_power); + serialPrint(LOG_VERBOSE, "Temperature: %d", ac_status->ac_temp); + serialPrint(LOG_VERBOSE, "Mode: %d", ac_status->ac_mode); + serialPrint(LOG_VERBOSE, "Wind Direction: %d", ac_status->ac_wind_dir); + serialPrint(LOG_VERBOSE, "Wind Speed: %d", ac_status->ac_wind_speed); + serialPrint(LOG_VERBOSE, "Display: %d", ac_status->ac_display); + serialPrint(LOG_VERBOSE, "Sleep: %d", ac_status->ac_sleep); + serialPrint(LOG_VERBOSE, "Timer: %d", ac_status->ac_timer); + serialPrint(LOG_VERBOSE, "Change Wind Direction: %d", ac_status->change_wind_direction); + serialPrint(LOG_VERBOSE, "Key Code: %d", *keyCode); + + return 0; +} \ No newline at end of file diff --git a/arduino-example/src/control_command.h b/arduino-example/src/control_command.h new file mode 100644 index 0000000..bb2a6dd --- /dev/null +++ b/arduino-example/src/control_command.h @@ -0,0 +1,40 @@ +/** +* + * Copyright (c) 2020-2025 IRext Opensource Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ARDUINO_EXAMPLE_CONTROL_COMMAND_H +#define ARDUINO_EXAMPLE_CONTROL_COMMAND_H + +#include "ir_decode.h" + +#ifdef __cplusplus +extern "C" { +#endif + +int parseControlCommand(int category, const char *commandJson, + t_remote_ac_status *ac_status, int* keyCode); + +#ifdef __cplusplus +} +#endif + +#endif // ARDUINO_EXAMPLE_CONTROL_COMMAND_H \ No newline at end of file diff --git a/arduino-example/src/ir_decode/include/ir_ac_apply.h b/arduino-example/src/ir_decode/include/ir_ac_apply.h index af68831..4c149cf 100644 --- a/arduino-example/src/ir_decode/include/ir_ac_apply.h +++ b/arduino-example/src/ir_decode/include/ir_ac_apply.h @@ -26,15 +26,15 @@ extern "C" #define MIN_TAG_LENGTH_TYPE_1 4 #define MIN_TAG_LENGTH_TYPE_2 6 -INT8 apply_power(t_remote_ac_status ac_status, UINT8 function_code); +INT8 apply_power(t_remote_ac_status *ac_status, UINT8 function_code); -INT8 apply_mode(t_remote_ac_status ac_status, UINT8 function_code); +INT8 apply_mode(t_remote_ac_status *ac_status, UINT8 function_code); -INT8 apply_wind_speed(t_remote_ac_status ac_status, UINT8 function_code); +INT8 apply_wind_speed(t_remote_ac_status *ac_status, UINT8 function_code); -INT8 apply_swing(t_remote_ac_status ac_status, UINT8 function_code); +INT8 apply_swing(t_remote_ac_status *ac_status, UINT8 function_code); -INT8 apply_temperature(t_remote_ac_status ac_status, UINT8 function_code); +INT8 apply_temperature(t_remote_ac_status *ac_status, UINT8 function_code); INT8 apply_function(struct ac_protocol *protocol, UINT8 function); diff --git a/arduino-example/src/ir_decode/include/ir_ac_control.h b/arduino-example/src/ir_decode/include/ir_ac_control.h index 68bf99f..16d8406 100644 --- a/arduino-example/src/ir_decode/include/ir_ac_control.h +++ b/arduino-example/src/ir_decode/include/ir_ac_control.h @@ -342,7 +342,7 @@ typedef struct ac_protocol UINT8 swing_status; - BOOL change_wind_direction; + UINT8 change_wind_direction; UINT16 dc_cnt; t_ac_bit_num bit_num[MAX_BITNUM]; @@ -382,10 +382,11 @@ typedef struct REMOTE_AC_STATUS UINT8 ac_display; UINT8 ac_sleep; UINT8 ac_timer; + UINT8 change_wind_direction; } t_remote_ac_status; // function polymorphism -typedef INT8 (*lp_apply_ac_parameter)(t_remote_ac_status ac_status, UINT8 function_code); +typedef INT8 (*lp_apply_ac_parameter)(t_remote_ac_status *ac_status, UINT8 function_code); #define TAG_AC_BOOT_CODE 1 #define TAG_AC_ZERO 2 diff --git a/arduino-example/src/ir_decode/include/ir_decode.h b/arduino-example/src/ir_decode/include/ir_decode.h index 6d7ebeb..e237e41 100644 --- a/arduino-example/src/ir_decode/include/ir_decode.h +++ b/arduino-example/src/ir_decode/include/ir_decode.h @@ -276,11 +276,10 @@ extern INT8 ir_binary_open(const UINT8 category, const UINT8 sub_category, UINT8 * parameters: key_code (in) - the code of pressed key * user_data (out) - output decoded data in INT16 array format * ac_status(in) - pointer to AC status (optional) - * change_wind_direction (in) - if control changes wind direction for AC (for AC only) * * returns: length of decoded data (0 indicates decode failure) */ -extern UINT16 ir_decode(UINT8 key_code, UINT16* user_data, t_remote_ac_status* ac_status, BOOL change_wind_direction); +extern UINT16 ir_decode(UINT8 key_code, UINT16* user_data, t_remote_ac_status* ac_status); /** * function ir_close @@ -362,7 +361,7 @@ extern void ir_lib_free_inner_buffer(); UINT16 ir_decode_combo(const UINT8 category, const UINT8 sub_category, UINT8* binary, UINT16 bin_length, UINT8 key_code, UINT16* user_data, - t_remote_ac_status* ac_status, BOOL change_wind_direction); + t_remote_ac_status* ac_status); #ifdef __cplusplus } diff --git a/arduino-example/src/ir_decode/include/ir_defs.h b/arduino-example/src/ir_decode/include/ir_defs.h index dc471ec..dc98d95 100644 --- a/arduino-example/src/ir_decode/include/ir_defs.h +++ b/arduino-example/src/ir_decode/include/ir_defs.h @@ -12,7 +12,7 @@ Revision log: #ifndef _IR_DEFS_H #define _IR_DEFS_H -#define IR_DECODE_LIB_VER "1.5.0" +#define IR_DECODE_LIB_VER "1.5.2" #if defined (BOARD_PC) #pragma ide diagnostic ignored "OCUnusedGlobalDeclarationInspection" @@ -29,6 +29,10 @@ extern "C" #define LOG_TAG "ir_decode" #endif +#if defined BOARD_ARDUINO +#include "serial_log.h" +#endif + #define TRUE 1 #define FALSE 0 @@ -59,10 +63,10 @@ void noprint(const char *fmt, ...); #define ir_strlen(A) strlen(A) #if ((defined BOARD_PC) || (defined BOARD_PC_JNI)) && (defined DEBUG) #define ir_printf(...) do { printf(__VA_ARGS__); fflush(stdout); } while(0) -#else -#define ir_printf noprint +#elif (defined BOARD_ARDUINO) +#define ir_printf(...) do { serialPrint(LOG_DEBUG, __VA_ARGS__); } while(0) #endif -#define USER_DATA_SIZE 1636 +#define USER_DATA_SIZE 2048 // #define USER_DATA_SIZE 4096 #ifdef __cplusplus diff --git a/arduino-example/src/ir_decode/ir_ac_apply.c b/arduino-example/src/ir_decode/ir_ac_apply.c index 1dc8881..08c2e21 100644 --- a/arduino-example/src/ir_decode/ir_ac_apply.c +++ b/arduino-example/src/ir_decode/ir_ac_apply.c @@ -13,8 +13,8 @@ Revision log: #pragma ide diagnostic ignored "hicpp-signed-bitwise" #endif -#include "include/ir_utils.h" -#include "include/ir_ac_apply.h" +#include "ir_utils.h" +#include "ir_ac_apply.h" static INT8 apply_ac_power(struct ac_protocol *protocol, UINT8 power_status); @@ -616,17 +616,17 @@ INT8 apply_checksum(struct ac_protocol *protocol) return IR_DECODE_SUCCEEDED; } -INT8 apply_power(t_remote_ac_status ac_status, UINT8 function_code) +INT8 apply_power(t_remote_ac_status *ac_status, UINT8 function_code) { (void) function_code; - apply_ac_power(context, ac_status.ac_power); + apply_ac_power(context, ac_status->ac_power); return IR_DECODE_SUCCEEDED; } -INT8 apply_mode(t_remote_ac_status ac_status, UINT8 function_code) +INT8 apply_mode(t_remote_ac_status *ac_status, UINT8 function_code) { (void) function_code; - if (IR_DECODE_FAILED == apply_ac_mode(context, ac_status.ac_mode)) + if (IR_DECODE_FAILED == apply_ac_mode(context, ac_status->ac_mode)) { // do not implement this mechanism since mode, temperature, wind // speed would have unspecified function @@ -639,16 +639,16 @@ INT8 apply_mode(t_remote_ac_status ac_status, UINT8 function_code) return IR_DECODE_SUCCEEDED; } -INT8 apply_wind_speed(t_remote_ac_status ac_status, UINT8 function_code) +INT8 apply_wind_speed(t_remote_ac_status *ac_status, UINT8 function_code) { - if (FALSE == context->n_mode[ac_status.ac_mode].all_speed) + if (FALSE == context->n_mode[ac_status->ac_mode].all_speed) { // if this level is not in black list - if (!is_in(context->n_mode[ac_status.ac_mode].speed, - ac_status.ac_wind_speed, - context->n_mode[ac_status.ac_mode].speed_cnt)) + if (!is_in(context->n_mode[ac_status->ac_mode].speed, + ac_status->ac_wind_speed, + context->n_mode[ac_status->ac_mode].speed_cnt)) { - if (IR_DECODE_FAILED == apply_ac_wind_speed(context, ac_status.ac_wind_speed) && + if (IR_DECODE_FAILED == apply_ac_wind_speed(context, ac_status->ac_wind_speed) && function_code == AC_FUNCTION_WIND_SPEED) { // do not implement this mechanism since mode, temperature, wind @@ -689,7 +689,7 @@ INT8 apply_wind_speed(t_remote_ac_status ac_status, UINT8 function_code) return IR_DECODE_SUCCEEDED; } -INT8 apply_swing(t_remote_ac_status ac_status, UINT8 function_code) +INT8 apply_swing(t_remote_ac_status *ac_status, UINT8 function_code) { (void) ac_status; if (function_code == AC_FUNCTION_WIND_FIX) @@ -697,7 +697,7 @@ INT8 apply_swing(t_remote_ac_status ac_status, UINT8 function_code) // adjust fixed wind direction according to current status if (context->si.type == SWING_TYPE_NORMAL && context->si.mode_count > 1) { - if (TRUE == context->change_wind_direction) + if (1 == context->change_wind_direction) { context->si.dir_index++; } @@ -735,15 +735,15 @@ INT8 apply_swing(t_remote_ac_status ac_status, UINT8 function_code) return IR_DECODE_SUCCEEDED; } -INT8 apply_temperature(t_remote_ac_status ac_status, UINT8 function_code) +INT8 apply_temperature(t_remote_ac_status *ac_status, UINT8 function_code) { - if (FALSE == context->n_mode[ac_status.ac_mode].all_temp) + if (FALSE == context->n_mode[ac_status->ac_mode].all_temp) { - if (!is_in(context->n_mode[ac_status.ac_mode].temp, - ac_status.ac_temp, - context->n_mode[ac_status.ac_mode].temp_cnt)) + if (!is_in(context->n_mode[ac_status->ac_mode].temp, + ac_status->ac_temp, + context->n_mode[ac_status->ac_mode].temp_cnt)) { - if (IR_DECODE_FAILED == apply_ac_temperature(context, ac_status.ac_temp)) + if (IR_DECODE_FAILED == apply_ac_temperature(context, ac_status->ac_temp)) { if (function_code == AC_FUNCTION_TEMPERATURE_UP /*&& FALSE == has_function(context, AC_FUNCTION_TEMPERATURE_UP)*/) diff --git a/arduino-example/src/ir_decode/ir_ac_binary_parse.c b/arduino-example/src/ir_decode/ir_ac_binary_parse.c index de1f016..4b8ee1e 100644 --- a/arduino-example/src/ir_decode/ir_ac_binary_parse.c +++ b/arduino-example/src/ir_decode/ir_ac_binary_parse.c @@ -9,8 +9,8 @@ Revision log: * 2017-01-03: created by strawmanbobi **************************************************************************************/ -#include "include/ir_ac_binary_parse.h" -#include "include/ir_decode.h" +#include "ir_ac_binary_parse.h" +#include "ir_decode.h" UINT16 tag_head_offset = 0; diff --git a/arduino-example/src/ir_decode/ir_ac_build_frame.c b/arduino-example/src/ir_decode/ir_ac_build_frame.c index 366e08c..38c4d18 100644 --- a/arduino-example/src/ir_decode/ir_ac_build_frame.c +++ b/arduino-example/src/ir_decode/ir_ac_build_frame.c @@ -14,8 +14,8 @@ Revision log: #pragma ide diagnostic ignored "readability-redundant-declaration" #endif -#include "include/ir_ac_build_frame.h" -#include "include/ir_decode.h" +#include "ir_ac_build_frame.h" +#include "ir_decode.h" extern t_ac_protocol* context; diff --git a/arduino-example/src/ir_decode/ir_ac_control.c b/arduino-example/src/ir_decode/ir_ac_control.c index e6ee7b7..b012968 100644 --- a/arduino-example/src/ir_decode/ir_ac_control.c +++ b/arduino-example/src/ir_decode/ir_ac_control.c @@ -13,13 +13,13 @@ Revision log: #include #include -#include "include/ir_ac_control.h" -#include "include/ir_ac_binary_parse.h" -#include "include/ir_decode.h" -#include "include/ir_ac_parse_parameter.h" -#include "include/ir_ac_parse_forbidden_info.h" -#include "include/ir_ac_parse_frame_info.h" -#include "include/ir_utils.h" +#include "ir_ac_control.h" +#include "ir_ac_binary_parse.h" +#include "ir_decode.h" +#include "ir_ac_parse_parameter.h" +#include "ir_ac_parse_forbidden_info.h" +#include "ir_ac_parse_frame_info.h" +#include "ir_utils.h" #if defined USE_DYNAMIC_TAG diff --git a/arduino-example/src/ir_decode/ir_ac_parse_forbidden_info.c b/arduino-example/src/ir_decode/ir_ac_parse_forbidden_info.c index 5121932..64e5991 100644 --- a/arduino-example/src/ir_decode/ir_ac_parse_forbidden_info.c +++ b/arduino-example/src/ir_decode/ir_ac_parse_forbidden_info.c @@ -17,8 +17,8 @@ Revision log: #include #include -#include "include/ir_decode.h" -#include "include/ir_ac_parse_forbidden_info.h" +#include "ir_decode.h" +#include "ir_ac_parse_forbidden_info.h" extern t_ac_protocol *context; diff --git a/arduino-example/src/ir_decode/ir_ac_parse_frame_info.c b/arduino-example/src/ir_decode/ir_ac_parse_frame_info.c index d103815..b416ce8 100644 --- a/arduino-example/src/ir_decode/ir_ac_parse_frame_info.c +++ b/arduino-example/src/ir_decode/ir_ac_parse_frame_info.c @@ -10,11 +10,10 @@ Revision log: **************************************************************************************/ #include -#include #include -#include "include/ir_utils.h" -#include "include/ir_ac_parse_frame_info.h" +#include "ir_utils.h" +#include "ir_ac_parse_frame_info.h" INT8 parse_boot_code(struct tag_head *tag) @@ -341,5 +340,13 @@ INT8 parse_bit_num(struct tag_head *tag) if (context->bit_num[i].pos == -1) context->bit_num[i].pos = (UINT16) (context->default_code.len - 1); //convert -1 to last data pos } + + // Ensure bit_num_cnt does not exceed MAX_BITNUM to prevent buffer overflow + if (context->bit_num_cnt > MAX_BITNUM) + { + ir_printf("Warning: bit_num_cnt (%d) exceeds MAX_BITNUM (%d), limiting to MAX_BITNUM\n", + context->bit_num_cnt, MAX_BITNUM); + context->bit_num_cnt = MAX_BITNUM; + } return IR_DECODE_SUCCEEDED; } diff --git a/arduino-example/src/ir_decode/ir_ac_parse_parameter.c b/arduino-example/src/ir_decode/ir_ac_parse_parameter.c index d8d2a03..97aeba6 100644 --- a/arduino-example/src/ir_decode/ir_ac_parse_parameter.c +++ b/arduino-example/src/ir_decode/ir_ac_parse_parameter.c @@ -13,8 +13,8 @@ Revision log: #include #include -#include "include/ir_utils.h" -#include "include/ir_ac_parse_parameter.h" +#include "ir_utils.h" +#include "ir_ac_parse_parameter.h" static INT8 parse_checksum_byte_typed(const UINT8 *csdata, t_tag_checksum_data *checksum, UINT16 len); @@ -236,6 +236,13 @@ INT8 parse_temp_1(struct tag_head *tag, t_temp_1 *temp1) temp1->len = (UINT8) hex_len; UINT8 seg_len = hex_data[0]; + // Initialize all segments to NULL to ensure proper cleanup in case of error + for (seg_index = AC_TEMP_16; seg_index < (UINT16) AC_TEMP_MAX; seg_index++) + { + temp1->comp_data[seg_index].seg_len = 0; + temp1->comp_data[seg_index].segment = NULL; + } + for (seg_index = AC_TEMP_16; seg_index < (UINT16) AC_TEMP_MAX; seg_index++) { // 020210 indicates set the 02nd byte to [default] +10, +11, +12, +... @@ -243,6 +250,17 @@ INT8 parse_temp_1(struct tag_head *tag, t_temp_1 *temp1) temp1->comp_data[seg_index].segment = (UINT8 *) ir_malloc(seg_len); if (NULL == temp1->comp_data[seg_index].segment) { + // Clean up previously allocated memory + UINT16 cleanup_idx; + for (cleanup_idx = AC_TEMP_16; cleanup_idx < seg_index; cleanup_idx++) + { + if (temp1->comp_data[cleanup_idx].segment != NULL) + { + ir_free(temp1->comp_data[cleanup_idx].segment); + temp1->comp_data[cleanup_idx].segment = NULL; + temp1->comp_data[cleanup_idx].seg_len = 0; + } + } ir_free(hex_data); return IR_DECODE_FAILED; } @@ -400,10 +418,30 @@ INT8 parse_swing_1(struct tag_head *tag, t_swing_1 *swing1, UINT16 swing_count) return IR_DECODE_FAILED; } + // Initialize the comp_data to ensure proper cleanup in case of error + for (seg_index = 0; seg_index < swing_count; seg_index++) + { + swing1->comp_data[seg_index].seg_len = 0; + swing1->comp_data[seg_index].segment = NULL; + } + for (seg_index = 0; seg_index < swing_count; seg_index++) { if (IR_DECODE_FAILED == parse_comp_data_type_1(hex_data, &trav_offset, &swing1->comp_data[seg_index])) { + // Clean up any allocated memory in previous iterations + UINT16 i; + for (i = 0; i < seg_index; i++) + { + if (swing1->comp_data[i].segment != NULL) + { + ir_free(swing1->comp_data[i].segment); + swing1->comp_data[i].segment = NULL; + swing1->comp_data[i].seg_len = 0; + } + } + ir_free(swing1->comp_data); + swing1->comp_data = NULL; ir_free(hex_data); return IR_DECODE_FAILED; } @@ -518,6 +556,16 @@ INT8 parse_checksum(struct tag_head *tag, t_checksum *checksum) checksum->checksum_data + num, (UINT8) (i - preindex) >> (UINT8) 1)) { + // Clean up allocated memory on error + UINT16 j; + for (j = 0; j < num; j++) { + if (checksum->checksum_data[j].spec_pos != NULL) { + ir_free(checksum->checksum_data[j].spec_pos); + checksum->checksum_data[j].spec_pos = NULL; + } + } + ir_free(checksum->checksum_data); + checksum->checksum_data = NULL; return IR_DECODE_FAILED; } preindex = (UINT16) (i + 1); @@ -529,6 +577,16 @@ INT8 parse_checksum(struct tag_head *tag, t_checksum *checksum) checksum->checksum_data + num, (UINT8) (i - preindex) >> (UINT8) 1)) { + // Clean up allocated memory on error + UINT16 j; + for (j = 0; j <= num; j++) { + if (checksum->checksum_data[j].spec_pos != NULL) { + ir_free(checksum->checksum_data[j].spec_pos); + checksum->checksum_data[j].spec_pos = NULL; + } + } + ir_free(checksum->checksum_data); + checksum->checksum_data = NULL; return IR_DECODE_FAILED; } @@ -635,18 +693,12 @@ INT8 parse_function_1_tag29(struct tag_head *tag, t_function_1 *function1) // seg_index in TAG only refers to functional count for (seg_index = AC_FUNCTION_POWER; seg_index < (UINT16) AC_FUNCTION_MAX; seg_index++) { - /** WARNING: for strict mode only **/ - /** - INT8 fid = parse_function_1(hex_data, &trav_offset, &function1->comp_data[0]); - if (fid > AC_FUNCTION_MAX - 1) + INT8 result = parse_function_1(hex_data, &trav_offset, &function1->comp_data[0]); + if (result == IR_DECODE_FAILED) { - irda_free(hex_data); - hex_data = NULL; + ir_free(hex_data); return IR_DECODE_FAILED; } - **/ - - parse_function_1(hex_data, &trav_offset, &function1->comp_data[0]); if (trav_offset >= hex_len) { break; @@ -694,6 +746,13 @@ INT8 parse_temp_2(struct tag_head *tag, t_temp_2 *temp2) temp2->len = (UINT8) hex_len; UINT8 seg_len = hex_data[0]; + // Initialize all segments to NULL to ensure proper cleanup in case of error + for (seg_index = AC_TEMP_16; seg_index < (UINT16) AC_TEMP_MAX; seg_index++) + { + temp2->comp_data[seg_index].seg_len = 0; + temp2->comp_data[seg_index].segment = NULL; + } + for (seg_index = AC_TEMP_16; seg_index < (UINT16) AC_TEMP_MAX; seg_index++) { // 020210 indicates set the 02nd byte to [default] +10, +11, +12, +... @@ -701,6 +760,17 @@ INT8 parse_temp_2(struct tag_head *tag, t_temp_2 *temp2) temp2->comp_data[seg_index].segment = (UINT8 *) ir_malloc(seg_len); if (NULL == temp2->comp_data[seg_index].segment) { + // Clean up previously allocated memory + UINT16 cleanup_idx; + for (cleanup_idx = AC_TEMP_16; cleanup_idx < seg_index; cleanup_idx++) + { + if (temp2->comp_data[cleanup_idx].segment != NULL) + { + ir_free(temp2->comp_data[cleanup_idx].segment); + temp2->comp_data[cleanup_idx].segment = NULL; + temp2->comp_data[cleanup_idx].seg_len = 0; + } + } ir_free(hex_data); return IR_DECODE_FAILED; } @@ -873,10 +943,30 @@ INT8 parse_swing_2(struct tag_head *tag, t_swing_2 *swing2, UINT16 swing_count) return IR_DECODE_FAILED; } + // Initialize the comp_data to ensure proper cleanup in case of error + for (seg_index = 0; seg_index < swing_count; seg_index++) + { + swing2->comp_data[seg_index].seg_len = 0; + swing2->comp_data[seg_index].segment = NULL; + } + for (seg_index = 0; seg_index < swing_count; seg_index++) { if (IR_DECODE_FAILED == parse_comp_data_type_2(hex_data, &trav_offset, &swing2->comp_data[seg_index])) { + // Clean up any allocated memory in previous iterations + UINT16 i; + for (i = 0; i < seg_index; i++) + { + if (swing2->comp_data[i].segment != NULL) + { + ir_free(swing2->comp_data[i].segment); + swing2->comp_data[i].segment = NULL; + swing2->comp_data[i].seg_len = 0; + } + } + ir_free(swing2->comp_data); + swing2->comp_data = NULL; ir_free(hex_data); return IR_DECODE_FAILED; } @@ -993,18 +1083,12 @@ INT8 parse_function_2_tag34(struct tag_head *tag, t_function_2 *function2) // seg_index in TAG only refers to functional count for (seg_index = AC_FUNCTION_POWER; seg_index < (UINT16) AC_FUNCTION_MAX; seg_index++) { - /** WARNING: for strict mode only **/ - /** - INT8 fid = parse_function_2(hex_data, &trav_offset, &function2->comp_data[0]); - if (fid > AC_FUNCTION_MAX - 1) + INT8 result = parse_function_2(hex_data, &trav_offset, &function2->comp_data[0]); + if (result == IR_DECODE_FAILED) { - irda_free(hex_data); - hex_data = NULL; + ir_free(hex_data); return IR_DECODE_FAILED; } - **/ - - parse_function_2(hex_data, &trav_offset, &function2->comp_data[0]); if (trav_offset >= hex_len) { break; diff --git a/arduino-example/src/ir_decode/ir_decode.c b/arduino-example/src/ir_decode/ir_decode.c index 0865f42..eb314fb 100644 --- a/arduino-example/src/ir_decode/ir_decode.c +++ b/arduino-example/src/ir_decode/ir_decode.c @@ -14,10 +14,10 @@ Revision log: #include -#include "include/ir_decode.h" -#include "include/ir_utils.h" -#include "include/ir_ac_build_frame.h" -#include "include/ir_ac_apply.h" +#include "ir_decode.h" +#include "ir_utils.h" +#include "ir_ac_build_frame.h" +#include "ir_ac_apply.h" struct ir_bin_buffer binary_file; struct ir_bin_buffer *p_ir_buffer = &binary_file; @@ -79,18 +79,23 @@ static INT8 ir_ac_file_open(const char *file_name); #endif static INT8 ir_ac_binary_open(UINT8 *binary, UINT16 bin_length); -static UINT16 ir_ac_control(t_remote_ac_status ac_status, UINT16* user_data, UINT8 key_code, - BOOL change_wind_direction); + +static UINT16 ir_ac_control(t_remote_ac_status *ac_status, UINT16* user_data, UINT8 key_code); + static INT8 ir_ac_binary_close(); -static BOOL validate_ac_status(t_remote_ac_status* ac_status, BOOL change_wind_dir); + +static BOOL validate_ac_status(t_remote_ac_status* ac_status); #if !defined NO_FS static INT8 ir_tv_file_open(const char *file_name); #endif static INT8 ir_tv_binary_open(UINT8 *binary, UINT16 bin_length); + static INT8 ir_tv_binary_parse(UINT8 ir_hex_encode); + static UINT16 ir_tv_control(UINT8 key, UINT16 *l_user_data); + static INT8 ir_tv_binary_close(); @@ -244,8 +249,7 @@ INT8 ir_binary_open(const UINT8 category, const UINT8 sub_category, UINT8* binar } /** the main entry of decode algorithm **/ -UINT16 ir_decode(UINT8 key_code, UINT16* user_data, - t_remote_ac_status* ac_status, BOOL change_wind_direction) +UINT16 ir_decode(UINT8 key_code, UINT16* user_data, t_remote_ac_status* ac_status) { ir_printf("remote_category = %d, KEY_CODE_MAX = %d\n", remote_category, KEY_CODE_MAX[remote_category]); @@ -266,21 +270,21 @@ UINT16 ir_decode(UINT8 key_code, UINT16* user_data, return 0; } ir_printf("ac status is not null in decode core : power = %d, mode = %d, " - "temp = %d, wind_dir = %d, wind_speed = %d, " - "key_code = %d, change_wind_direction = %d\n", + "temp = %d, wind_dir = %d, wind_speed = %d, change_wind_direction = %d, " + "key_code = %d\n", ac_status->ac_power, ac_status->ac_mode, ac_status->ac_temp, ac_status->ac_wind_dir, ac_status->ac_wind_speed, - key_code, change_wind_direction); + ac_status->change_wind_direction, + key_code); // ac status validation - if (FALSE == validate_ac_status(ac_status, change_wind_direction)) { + if (FALSE == validate_ac_status(ac_status)) { return 0; } - return ir_ac_control(*ac_status, user_data, key_code, change_wind_direction); + return ir_ac_control(ac_status, user_data, key_code); } } - INT8 ir_close() { if (IR_TYPE_COMMANDS == ir_binary_type) @@ -359,8 +363,7 @@ static INT8 ir_ac_binary_open(UINT8 *binary, UINT16 bin_length) return IR_DECODE_SUCCEEDED; } -static UINT16 ir_ac_control(t_remote_ac_status ac_status, UINT16* user_data, UINT8 key_code, - BOOL change_wind_direction) +static UINT16 ir_ac_control(t_remote_ac_status *ac_status, UINT16* user_data, UINT8 key_code) { UINT16 time_length = 0; UINT8 function_code = 0; @@ -402,7 +405,7 @@ static UINT16 ir_ac_control(t_remote_ac_status ac_status, UINT16* user_data, UIN } // pre-set change wind direction flag here - context->change_wind_direction = change_wind_direction; + context->change_wind_direction = ac_status->change_wind_direction; context->time = user_data; @@ -410,15 +413,16 @@ static UINT16 ir_ac_control(t_remote_ac_status ac_status, UINT16* user_data, UIN ir_memcpy(ir_hex_code, context->default_code.data, context->default_code.len); #if defined USE_APPLY_TABLE - if(ac_status.ac_power != AC_POWER_OFF) + if(ac_status->ac_power != AC_POWER_OFF) { + UINT8 i; for (i = AC_APPLY_POWER; i < AC_APPLY_MAX; i++) { - apply_table[i](context, parameter_array[i]); + apply_table[i](ac_status, function_code); } } #else - if (ac_status.ac_power == AC_POWER_OFF) + if (ac_status->ac_power == AC_POWER_OFF) { // otherwise, power should always be applied apply_power(ac_status, function_code); @@ -426,7 +430,7 @@ static UINT16 ir_ac_control(t_remote_ac_status ac_status, UINT16* user_data, UIN else { // check the mode as the first priority, despite any other status - if (TRUE == context->n_mode[ac_status.ac_mode].enable) + if (TRUE == context->n_mode[ac_status->ac_mode].enable) { if (is_solo_function(function_code)) { @@ -506,7 +510,7 @@ static INT8 ir_ac_binary_close() return IR_DECODE_SUCCEEDED; } -static BOOL validate_ac_status(t_remote_ac_status* ac_status, BOOL change_wind_dir) +static BOOL validate_ac_status(t_remote_ac_status* ac_status) { if (AC_POWER_OFF != ac_status->ac_power && AC_POWER_ON != ac_status->ac_power) { @@ -528,7 +532,7 @@ static BOOL validate_ac_status(t_remote_ac_status* ac_status, BOOL change_wind_d { return FALSE; } - if (0 != change_wind_dir && 1 != change_wind_dir) + if (0 != ac_status->change_wind_direction && 1 != ac_status->change_wind_direction) { return FALSE; } @@ -776,7 +780,7 @@ static INT8 ir_tv_binary_close() UINT16 ir_decode_combo(const UINT8 category, const UINT8 sub_category, UINT8* binary, UINT16 bin_length, UINT8 key_code, UINT16* user_data, - t_remote_ac_status* ac_status, BOOL change_wind_direction) + t_remote_ac_status* ac_status) { UINT16 decoded_length = 0; @@ -787,6 +791,13 @@ UINT16 ir_decode_combo(const UINT8 category, const UINT8 sub_category, return IR_DECODE_FAILED; } + if (sub_category < SUB_CATEGORY_QUATERNARY || + sub_category >= SUB_CATEGORY_NEXT) + { + ir_printf("wrong remote sub category : %d\n", sub_category); + return IR_DECODE_FAILED; + } + remote_category = (t_remote_category) category; if (key_code < 0 || key_code >= KEY_CODE_MAX[remote_category]) @@ -798,7 +809,7 @@ UINT16 ir_decode_combo(const UINT8 category, const UINT8 sub_category, if (IR_DECODE_SUCCEEDED == ir_binary_open(category, sub_category, binary, bin_length)) { - decoded_length = ir_decode(key_code, user_data, ac_status, change_wind_direction); + decoded_length = ir_decode(key_code, user_data, ac_status); ir_close(); return decoded_length; } diff --git a/arduino-example/src/ir_decode/ir_tv_control.c b/arduino-example/src/ir_decode/ir_tv_control.c index 53a3e80..e922dc3 100644 --- a/arduino-example/src/ir_decode/ir_tv_control.c +++ b/arduino-example/src/ir_decode/ir_tv_control.c @@ -15,9 +15,9 @@ Revision log: #include -#include "include/ir_defs.h" -#include "include/ir_decode.h" -#include "include/ir_tv_control.h" +#include "ir_defs.h" +#include "ir_decode.h" +#include "ir_tv_control.h" struct buffer @@ -249,8 +249,16 @@ static void print_ir_time(t_ir_data *data, UINT8 key_index, UINT16 *ir_time) } else if (ir_level == IRDA_LEVEL_LOW) { + if (time_index + 1 > USER_DATA_SIZE) { + ir_printf("time index exceeded\n"); + return; + } ir_time[time_index++] = pcycles->mask; } + if (time_index + 1 > USER_DATA_SIZE) { + ir_printf("time index exceeded\n"); + return; + } ir_time[time_index++] = pcycles->space; ir_level = IRDA_LEVEL_LOW; } @@ -263,8 +271,16 @@ static void print_ir_time(t_ir_data *data, UINT8 key_index, UINT16 *ir_time) } else if (ir_level == IRDA_LEVEL_HIGH) { + if (time_index + 1 > USER_DATA_SIZE) { + ir_printf("time index exceeded\n"); + return; + } ir_time[time_index++] = pcycles->space; } + if (time_index + 1 > USER_DATA_SIZE) { + ir_printf("time index exceeded\n"); + return; + } ir_time[time_index++] = pcycles->mask; ir_level = IRDA_LEVEL_HIGH; } @@ -278,6 +294,10 @@ static void print_ir_time(t_ir_data *data, UINT8 key_index, UINT16 *ir_time) } else if (ir_level == IRDA_LEVEL_HIGH) { + if (time_index + 1 > USER_DATA_SIZE) { + ir_printf("time index exceeded\n"); + return; + } ir_time[time_index++] = pcycles->space; } ir_level = IRDA_LEVEL_LOW; @@ -291,6 +311,10 @@ static void print_ir_time(t_ir_data *data, UINT8 key_index, UINT16 *ir_time) } else if (ir_level == IRDA_LEVEL_LOW) { + if (time_index + 1 > USER_DATA_SIZE) { + ir_printf("time index exceeded\n"); + return; + } ir_time[time_index++] = pcycles->mask; } ir_level = IRDA_LEVEL_HIGH; diff --git a/arduino-example/src/ir_decode/ir_utils.c b/arduino-example/src/ir_decode/ir_utils.c index 9045a52..7d90ea5 100644 --- a/arduino-example/src/ir_decode/ir_utils.c +++ b/arduino-example/src/ir_decode/ir_utils.c @@ -9,7 +9,7 @@ Revision log: * 2016-10-01: created by strawmanbobi **************************************************************************************/ -#include "include/ir_utils.h" +#include "ir_utils.h" UINT8 char_to_hex(char chr) { diff --git a/arduino-example/src/main.cpp b/arduino-example/src/main.cpp index 4522214..b9cd17e 100644 --- a/arduino-example/src/main.cpp +++ b/arduino-example/src/main.cpp @@ -28,6 +28,7 @@ #include "configure.h" #include "remote.h" +#include "serial_log.h" #define WIFI_SERVER_PORT (8000) @@ -74,21 +75,18 @@ void printWiFiStatus() { unsigned long currentMillis = millis(); if (currentMillis - lastStatusCheck >= ALIVE_DEBUG_INTERVAL) { - IPAddress ip = WiFi.localIP(); + const IPAddress ip = WiFi.localIP(); if (0 == strcmp(ip.toString().c_str(), "0.0.0.0")) { lastStatusCheck = currentMillis; return; } - Serial.print("SSID: "); - Serial.println(WiFi.SSID()); + serialPrint(LOG_INFO, "Wi-Fi SSID: %s", WiFi.SSID()); - Serial.print("IP address: "); - Serial.println(ip); + serialPrint(LOG_INFO, "Wi-Fi IP address: %s", ip.toString().c_str()); const long rssi = WiFi.RSSI(); - Serial.print("Signal strength (RSSI): "); - Serial.print(rssi); - Serial.println(" dBm"); + serialPrint(LOG_INFO, "Wi-Fi signal strength (RSSI): %ld dBm", rssi); + lastStatusCheck = currentMillis; if (0 == wifiStatusPrinted) { @@ -98,12 +96,20 @@ void printWiFiStatus() { } } +static void sendToClient(WiFiClient *client, const char* content) { + client->println(content); + client->flush(); +} + void setup() { Serial.begin(115200); + while (!Serial) { delay(100); } + remoteInit(); + matrix.begin(); matrix.beginDraw(); @@ -117,33 +123,31 @@ void setup() { matrix.endText(SCROLL_LEFT); matrix.endDraw(); - Serial.println("IRext Arduino example started in station mode"); - Serial.print("Attempting to connect to SSID: "); - Serial.println(ssid); + serialPrint(LOG_INFO, "IRext Arduino example started in station mode"); + serialPrint(LOG_INFO, "Attempting to connect to SSID: %s", ssid); status = WiFi.begin(ssid, pass); if (status == WL_CONNECTED) { - Serial.println("\nConnected to Wi-Fi"); + serialPrint(LOG_INFO, "Connected to Wi-Fi"); server.begin(); } else { - Serial.print("\nFailed to connect Wi-Fi, status: "); - Serial.println(status); + serialPrint(LOG_ERROR, "Failed to connect Wi-Fi, status: %d", status); } } void onConnected(WiFiClient *client) { client->flush(); - Serial.println("Client connected"); - client->println(eHello); + serialPrint(LOG_DEBUG, "Client connected"); + sendToClient(client, eHello); } void onDisconnected(WiFiClient *client) { remoteClose(); client->flush(); client->stop(); - Serial.println("Client disconnected"); + serialPrint(LOG_DEBUG, "Client disconnected"); } void onError(WiFiClient *client) { @@ -151,54 +155,61 @@ void onError(WiFiClient *client) { client->stop(); } -void onCommand(const String *command, WiFiClient *client) { +void onCommand(WiFiClient *client, const String *command) { if (command->startsWith(aHello)) { - Serial.println("Received hello command"); - client->println(eBin); - client->flush(); + serialPrint(LOG_DEBUG, "Received hello command"); + sendToClient(client, eBin); } else if (command->startsWith(aBin)) { - Serial.println("Received bin command"); + serialPrint(LOG_DEBUG, "Received bin command"); +#if !defined TEST_BIN_RECEIVE if (remoteOpen(command->c_str()) > 0) { - client->println(eControl); + sendToClient(client, eControl); } else { - Serial.println("Failed to parse bin command"); - client->println(eError); + serialPrint(LOG_ERROR, "Failed to parse bin command"); + sendToClient(client, eError); } +#else + sendToClient(client, eControl); +#endif } else if (command->startsWith(aControl)) { - Serial.println("Received control command"); + serialPrint(LOG_DEBUG, "Received control command"); remoteControl(command->c_str()); } else if (command->startsWith(aError)) { - Serial.println("Received error command"); + serialPrint(LOG_DEBUG, "Received error command"); onError(client); } } void loop() { if (WiFi.status() != WL_CONNECTED) { - Serial.print("Connection lost. Reconnecting..."); + serialPrint(LOG_INFO, "Connection lost, reconnecting"); status = WiFi.begin(ssid, pass); if (status == WL_CONNECTED) { - Serial.println("Reconnected!"); + serialPrint(LOG_INFO, "Reconnected"); } } else { printWiFiStatus(); - client = server.available(); - if (client.connected()) { - if (false == clientConnected) { + if (!client || !client.connected()) { + client = server.available(); + if (client) { + clientConnected = true; onConnected(&client); } - clientConnected = true; + } + if (client && client.connected()) { if (client.available()) { - const String received = client.readStringUntil('\n'); - Serial.println(received); - onCommand(&received, &client); - } - } else { - if (clientConnected) { - onDisconnected(&client); + String received = client.readStringUntil('\n'); + received.trim(); + + if (received.length() > 0) { + serialPrint(LOG_VERBOSE, "Data received: %d", received.length()); + onCommand(&client, &received); + } } + } else if (clientConnected) { + onDisconnected(&client); clientConnected = false; + client.stop(); } } - delay(10); } diff --git a/arduino-example/src/remote.cpp b/arduino-example/src/remote.cpp index fcf1fac..81bb63f 100644 --- a/arduino-example/src/remote.cpp +++ b/arduino-example/src/remote.cpp @@ -23,89 +23,229 @@ #include #include +#include +#include "control_command.h" #include "utils.h" +#include "serial_log.h" #include "ir_decode.h" +#include "remote.h" -#define REMOTE_BIN_MAX (1024) +#define REMOTE_BIN_MAX (1024) -#define ABIN_COMMAND_SEG (5) -#define SEG_ABIN_HEADER (0) -#define SEG_ABIN_CATE (1) -#define SEG_ABIN_SUBCATE (2) -#define SEG_ABIN_LENGTH (3) -#define SEG_ABIN_BIN (4) +#define ABIN_COMMAND_SEG (5) +#define ACTRL_COMMAND_SEG (3) -// global variable definitions -unsigned char *remoteBin = nullptr; -int remoteBinLen = 0; +#define SEG_ABIN_HEADER (0) +#define SEG_ABIN_CATE (1) +#define SEG_ABIN_SUBCATE (2) +#define SEG_ABIN_LENGTH (3) +#define SEG_ABIN_BIN (4) +#define SEG_ACTRL_HEADER (0) +#define SEG_ACTRL_LENGTH (1) +#define SEG_ACTRL_COMMAND (2) + +#define IR_SEND_PIN 3 + + +// external variable declarations +extern char *eError; + + +// private variable definitions +static uint8_t categoryId = -1; +static uint8_t subCategoryId = -1; +static unsigned char *remoteBin = nullptr; +static int remoteBinLen = 0; +static uint16_t remoteUserData[USER_DATA_SIZE] = { 0 }; +static uint16_t userDataLen = 0; // public function definitions +void remoteInit() { + IrSender.begin(IR_SEND_PIN); +} + int remoteOpen(const char *binStr) { char *aBinCommand[ABIN_COMMAND_SEG]; char *remoteBinStr = nullptr; - int categoryId = 0; - int subCateId = 0; int aBinCommandSeg = 0; int remoteBinBase64Len = 0; + int retVal = 0; aBinCommandSeg = splitString(binStr, aBinCommand, ABIN_COMMAND_SEG, ","); if (ABIN_COMMAND_SEG != aBinCommandSeg) { - Serial.print("Invalid aBin command: "); - Serial.println(binStr); - return -1; + serialPrint(LOG_ERROR, "Invalid aBin command: %s", binStr); + retVal = -1; + goto _exit; } categoryId = strtol(aBinCommand[SEG_ABIN_CATE], nullptr, 10); - subCateId = strtol(aBinCommand[SEG_ABIN_SUBCATE], nullptr, 10); + subCategoryId = strtol(aBinCommand[SEG_ABIN_SUBCATE], nullptr, 10); remoteBinBase64Len = strtol(aBinCommand[SEG_ABIN_LENGTH], nullptr, 10); remoteBinStr = aBinCommand[SEG_ABIN_BIN]; if (remoteBinBase64Len != strlen(remoteBinStr)) { - Serial.println("Remote bin length not correct"); - return -1; + serialPrint(LOG_ERROR, "Remote bin length not correct, expected : %d, decoded : %d", + remoteBinBase64Len, remoteBinLen); + retVal = -1; + goto _exit; } remoteBinLen = base64_dec_len(remoteBinStr, remoteBinBase64Len); + + // free the previously used buffer + if (nullptr != remoteBin) { + free(remoteBin); + remoteBin = nullptr; + } remoteBin = static_cast(malloc(remoteBinLen)); - char debugStr[129]; if (nullptr == remoteBin) { - Serial.println("Not enough memory for remote bin"); - return -1; + serialPrint(LOG_ERROR, "Not enough memory for remote bin"); + retVal = -1; + goto _exit; } memset(remoteBin, 0, remoteBinLen); if (remoteBinLen != base64_decode(reinterpret_cast(remoteBin), remoteBinStr, remoteBinBase64Len)) { - Serial.println("Failed to decode remote bin"); - return -1; + serialPrint(LOG_ERROR, "Failed to decode remote bin"); + retVal = -1; + goto _exit; } -#if defined REMOTE_BIN_DEBUG - Serial.print("Remote bin length = "); - Serial.println(remoteBinLen); - snprintf(debugStr, 128, "%02x %02x %02x %02x %02x %02x %02x %02x", - remoteBin[0], remoteBin[1], remoteBin[2], remoteBin[3], - remoteBin[4], remoteBin[5], remoteBin[6], remoteBin[7]); - Serial.println(debugStr); - snprintf(debugStr, 128, "%02x %02x %02x %02x %02x %02x %02x %02x", - remoteBin[remoteBinLen - 8], remoteBin[remoteBinLen - 7], remoteBin[remoteBinLen - 6], remoteBin[remoteBinLen - 5], - remoteBin[remoteBinLen - 4], remoteBin[remoteBinLen - 3], remoteBin[remoteBinLen - 2], remoteBin[remoteBinLen - 1]); - Serial.println(debugStr); -#endif - - if (IR_DECODE_FAILED == ir_binary_open(categoryId, subCateId, remoteBin, remoteBinLen)) { - Serial.println("Failed to load remote bin"); - return -1; + if (getLogLevel() == LOG_VERBOSE) { + char debugStr[129] = { 0 }; + serialPrint(LOG_VERBOSE, "Remote bin(%d): ", remoteBinLen); + snprintf(debugStr, 128, "%02x %02x %02x %02x %02x %02x %02x %02x", + remoteBin[0], remoteBin[1], remoteBin[2], remoteBin[3], + remoteBin[4], remoteBin[5], remoteBin[6], remoteBin[7]); + serialPrint(LOG_VERBOSE, debugStr); + snprintf(debugStr, 128, "%02x %02x %02x %02x %02x %02x %02x %02x", + remoteBin[remoteBinLen - 8], remoteBin[remoteBinLen - 7], remoteBin[remoteBinLen - 6], remoteBin[remoteBinLen - 5], + remoteBin[remoteBinLen - 4], remoteBin[remoteBinLen - 3], remoteBin[remoteBinLen - 2], remoteBin[remoteBinLen - 1]); + serialPrint(LOG_VERBOSE, debugStr); } - Serial.println("Remote bin loaded successfully"); - return remoteBinLen; + if (IR_DECODE_FAILED == ir_binary_open(categoryId, subCategoryId, remoteBin, remoteBinLen)) { + serialPrint(LOG_ERROR, "Failed to load remote bin"); + retVal = -1; + goto _exit; + } + + retVal = remoteBinLen; + serialPrint(LOG_INFO, "Remote bin loaded successfully"); + +_exit: + return retVal; } int remoteControl(const char *controlStr) { - return 0; + char *aCtrlCommand[ACTRL_COMMAND_SEG]; + char *commandStrBase64 = nullptr; + char *commandStr = nullptr; + int aCtrlCommandSeg = 0; + int commandBase64Len = 0; + int commandLen = 0; + + t_remote_ac_status acStatus; + int keyCode = 0; + + int retVal = 0; + + aCtrlCommandSeg = splitString(controlStr, aCtrlCommand, ACTRL_COMMAND_SEG, ","); + if (ACTRL_COMMAND_SEG != aCtrlCommandSeg) { + serialPrint(LOG_ERROR, "Invalid aCtrl command: "); + retVal = -1; + goto _exit; + } + commandBase64Len = strtol(aCtrlCommand[SEG_ACTRL_LENGTH], nullptr, 10); + commandStrBase64 = aCtrlCommand[SEG_ACTRL_COMMAND]; + if (commandBase64Len != strlen(commandStrBase64)) { + serialPrint(LOG_ERROR, "Remote command length not correct, expected : %d, decoded : %d", + commandBase64Len, commandStrBase64); + retVal = -1; + goto _exit; + } + + commandLen = base64_dec_len(commandStrBase64, commandBase64Len); + commandStr = static_cast(malloc(commandLen)); + if (nullptr == commandStr) { + serialPrint(LOG_ERROR, "Not enough memory for remote command"); + retVal = -1; + goto _exit; + } + memset(commandStr, 0, commandLen); + + if (commandLen != base64_decode(commandStr, commandStrBase64, commandBase64Len)) { + serialPrint(LOG_ERROR, "Failed to decode remote command"); + retVal = -1; + goto _exit; + } + + serialPrint(LOG_DEBUG, "Received remote command: %s", commandStr); + + if (-1 != categoryId) { + if (0 != parseControlCommand(categoryId, commandStr, &acStatus, &keyCode)) { + serialPrint(LOG_ERROR, "Failed to parse command JSON"); + retVal = -1; + goto _exit; + } + } else { + serialPrint(LOG_ERROR, "No remote bin loaded"); + retVal = - 1; + goto _exit; + } + + userDataLen = ir_decode(keyCode, remoteUserData, &acStatus); + if (userDataLen > 0) { + serialPrint(LOG_INFO, "IR decoded successfully: %d", userDataLen); + } + if (getLogLevel() == LOG_VERBOSE) { + remoteDebug(remoteUserData, userDataLen); + } + + IrSender.sendRaw(remoteUserData, userDataLen, 38); + serialPrint(LOG_INFO, "IR sent successfully"); + + retVal = 0; +_exit: + + if (nullptr != commandStr) { + free(commandStr); + } + + return retVal; } void remoteClose() { + serialPrint(LOG_INFO, "Closing remote"); ir_close(); +} + + +// private function definitions +void remoteDebug(const uint16_t* userData, const uint16_t userDataLen) { + if (userData == nullptr || userDataLen == 0) { + serialPrint(LOG_VERBOSE, "userData is empty or null"); + return; + } + + char debugStr[256] = { 0 }; + int offset = 0; + + for (uint16_t i = 0; i < userDataLen; i++) { + if (i % 16 == 0) { + offset = snprintf(debugStr, sizeof(debugStr), "userData[%d-%d]: ", + i, (i + 15 < userDataLen) ? i + 15 : userDataLen - 1); + } + offset += snprintf(debugStr + offset, sizeof(debugStr) - offset, "%d ", userData[i]); + + if ((i + 1) % 16 == 0 || i == userDataLen - 1) { + serialPrint(LOG_VERBOSE, "%s", debugStr); + offset = 0; + } + } +} + +int irControlSend() { + } \ No newline at end of file diff --git a/arduino-example/src/remote.h b/arduino-example/src/remote.h index 29ed1c5..e863f3b 100644 --- a/arduino-example/src/remote.h +++ b/arduino-example/src/remote.h @@ -24,7 +24,11 @@ #ifndef ARDUINO_EXAMPLE_REMOTE_H #define ARDUINO_EXAMPLE_REMOTE_H -#define REMOTE_BIN_DEBUG (1) +#ifdef __cplusplus +extern "C" { +#endif + +void remoteInit(); int remoteOpen(const char *binStr); @@ -32,4 +36,9 @@ int remoteControl(const char *controlStr); void remoteClose(); -#endif //ARDUINO_EXAMPLE_REMOTE_H \ No newline at end of file +void remoteDebug(const uint16_t* userData, uint16_t userDataLen); + +#ifdef __cplusplus +} +#endif +#endif // ARDUINO_EXAMPLE_REMOTE_H \ No newline at end of file diff --git a/arduino-example/src/serial_log.cpp b/arduino-example/src/serial_log.cpp new file mode 100644 index 0000000..a4b9e44 --- /dev/null +++ b/arduino-example/src/serial_log.cpp @@ -0,0 +1,59 @@ +/** +* + * Copyright (c) 2020-2025 IRext Opensource Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include + +#include "serial_log.h" + +#define LOG_BUF_SIZE (128) + +// public variable definitions +int logLevel = LOG_VERBOSE; +char logBuf[LOG_BUF_SIZE] = { 0 }; + + +// public function definitions +void serialPrint(const int logType, const char* fmt, ...) { + if (logType < logLevel) { + return; + } + memset(logBuf, 0, LOG_BUF_SIZE); + + va_list ap; + va_start(ap, fmt); + vsnprintf(logBuf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + Serial.println(logBuf); +} + +void setLogLevel(const int level) { + logLevel = level; +} + +int getLogLevel() { + return logLevel; +} \ No newline at end of file diff --git a/arduino-example/src/serial_log.h b/arduino-example/src/serial_log.h new file mode 100644 index 0000000..1fbeb57 --- /dev/null +++ b/arduino-example/src/serial_log.h @@ -0,0 +1,45 @@ +/** +* + * Copyright (c) 2020-2025 IRext Opensource Organization + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ARDUINO_EXAMPLE_SERIAL_LOG_H +#define ARDUINO_EXAMPLE_SERIAL_LOG_H + +#define LOG_VERBOSE (0) +#define LOG_DEBUG (1) +#define LOG_INFO (2) +#define LOG_ERROR (3) + +#ifdef __cplusplus +extern "C" { +#endif + +void serialPrint(int logType, const char* fmt, ...); + +void setLogLevel(int level); + +int getLogLevel(); + +#ifdef __cplusplus +} +#endif +#endif // ARDUINO_EXAMPLE_SERIAL_LOG_H \ No newline at end of file diff --git a/arduino-example/src/utils.cpp b/arduino-example/src/utils.cpp index e7f72e8..d436da2 100644 --- a/arduino-example/src/utils.cpp +++ b/arduino-example/src/utils.cpp @@ -22,9 +22,9 @@ */ #include - #include +#include "utils.h" // public function definitions int splitString(const char *str, char *parts[], diff --git a/arduino-example/src/utils.h b/arduino-example/src/utils.h index 3a4b885..cb3856f 100644 --- a/arduino-example/src/utils.h +++ b/arduino-example/src/utils.h @@ -24,7 +24,15 @@ #ifndef ARDUINO_EXAMPLE_UTILS_H #define ARDUINO_EXAMPLE_UTILS_H +#ifdef __cplusplus +extern "C" { +#endif + int splitString(const char *str, char *parts[], int parts_max, const char *delimiter); +#ifdef __cplusplus +} +#endif + #endif //ARDUINO_EXAMPLE_UTILS_H \ No newline at end of file