This time we wire up Node-RED to call FlashAir, which in turn kicks Arduino to fire the IR transmission.
- Gather parts.
- Set up Node-RED on the main Raspberry Pi and register it with Amazon Echo Dot
- Headless WiFi setup for Raspberry Pi Zero W
- Copy IR remote codes with Raspberry Pi
- Call IR functions from Node-RED to control the TV
- Control Arduino via HTTP API using FlashAir GPIO mode
- (Bonus) Control Roomba from Arduino via ROI serial interface
- Control Roomba from Arduino via IR
- Control Roomba from Node-RED via FlashAir/Arduino [This article]
FlashAir GPIO
FlashAir can use each SD card pin as a GPIO pin.See FlashAir Developers for details.
As preparation, add "IFMODE=1" to the "CONFIG" file in FlashAir's SD_WLAN folder.
CONFIG is in a hidden folder, but on Mac or Linux you can access it from Terminal without any special tools.
On Windows, use any tool that can show hidden folders.
For this build I used the same SD card slot DIP adapter from Akizuki Denshi that FlashAir Developers recommends.
One thing that catches people off guard: this DIP adapter does not expose all of FlashAir's pins.
That means not all FlashAir GPIO pins are accessible.
Specifically, as shown in the DIP adapter schematic, DAT1 and DAT2 are only connected to 3.3V via pull-up resistors — their pads are not broken out.
So FlashAir bits 0x04 and 0x08 are unavailable without soldering jumpers.
The pin mappings available without soldering are shown below.
The "Arduino" column shows which Arduino pin was used, and "Purpose" shows the function assigned in this build.
Bit
MaskFlashAir DIP
ModuleArduino Purpose 0x01CMDSDID5Roomba ON 0x02DAT0SDOD4Sleep enable/disable 0x10DAT3CSD2Software reset -3.3VVCC3.3VPower to FlashAir -GNDGNDGNDGND
Arduino and FlashAir Wiring
Using the wiring above, the Arduino is programmed to behave as follows:- When pin D2 changes state, a software reset wakes Arduino from sleep.
- If pin D5 is HIGH, send IR codes to the Roomba.
- If pin D4 is HIGH, enter sleep mode.
That said, with DAT2 and DAT3 broken out, you can represent 3 bits of commands over WiFi,
giving you 8 possible IR codes (2³).
The CMD pin was assigned to D5 with that future expansion in mind.
Node-RED to FlashAir
From Node-RED, send HTTP requests to FlashAir in this sequence to trigger Arduino:- Set all FlashAir pins LOW — send 0x00 to FlashAir. (http://~/command.cgi?op=190&CTRL=0x1f&DATA=0x00)
- Wait one second (to ensure FlashAir finishes processing step 1).
- Set FlashAir bits 0x10, 0x01, 0x02 HIGH — send 0x13 to FlashAir. (http://~/command.cgi?op=190&CTRL=0x1f&DATA=0x13)
That LOW-to-HIGH change on Arduino's reset pin wakes it from sleep.
On wakeup, Arduino checks CMD (0x01) and, if HIGH, sends the IR code to start the Roomba.
After that, Arduino checks DAT0 (0x02) and goes back to sleep when it's HIGH, saving power.
Logically, LOW-to-sleep would be more power-efficient — but FlashAir sets all GPIO pins HIGH at power-on.
If sleep was triggered by DAT0=LOW, the Arduino would never sleep at startup.
Assigning DAT0=HIGH as the sleep trigger means Arduino sleeps immediately on power-on, which is what we want.
Arduino Code
The full Arduino sketch implementing the above logic. IR recording and transmission are the same as last time and omitted here.#include <EEPROM.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
// Arduino - board - SD
// 2 pin - CS - DAT3 - 1pin - 0x10 (RESET)
// 4 pin - SDO - DAT0 - 7pin - 0x02 (SLEEP)
// 5 pin - SDI - CMD - 2pin - 0x01 (CLEAN)
#define PIN_IR 10
#define PIN_IR_SENSOR 11
// FlashAir SDD pin (0x02)
#define PIN_SD_SLEEP 4
// FlashAir SDI pin (0x01)
#define PIN_SD_CLEAN 5
#define SCAN_INITIAL_TIMEOUT 10000000
#define SCAN_TIMEOUT 10000000
#define IR_BUF_LEN 64
volatile int IR_BUF[IR_BUF_LEN];
volatile bool SETUP_FLAG = false;
void setup() {
Serial.begin(9600);
SETUP_FLAG = true;
pinMode(PIN_IR, OUTPUT);
pinMode(PIN_IR_SENSOR, INPUT);
pinMode(PIN_SD_CLEAN, INPUT);
pinMode(PIN_SD_SLEEP, INPUT);
pinMode(2, INPUT_PULLUP);
Serial.println("Start!");
}
// Nothing to do on reset wakeup — just let loop() run
void wakeup(){
Serial.println("Wakeup");
}
// Ignore initial HIGH state at power-on via SETUP_FLAG.
// Cleaning starts only after a LOW-to-HIGH transition on PIN_SD_CLEAN.
void loop() {
Serial.println("Run");
if ( ! SETUP_FLAG ) {
if ( digitalRead(PIN_SD_CLEAN) == HIGH ){
Serial.println("Run Roomba in clean mode");
loadBuffer(0);
executeIR();
executeIR();
executeIR();
}
// Add more commands here if additional FlashAir pins are broken out
}
SETUP_FLAG = false;
delay(500);
check_sleep(digitalRead(PIN_SD_SLEEP));
delay(500);
}
// Sleep when sleep pin is HIGH
void check_sleep(bool flag) {
if ( ! flag ) { return; }
Serial.println("Sleep");
delay(1000);
// Wake up when reset pin changes state
attachInterrupt(0, wakeup, CHANGE);
set_sleep_mode(SLEEP_MODE_STANDBY);
sleep_enable();
sleep_mode();
sleep_disable();
}
Done
That's the full system complete.TV, projector, and Roomba can all be voice-controlled.
Not covered here, but with a bit of Node-RED flow design you can trigger multiple devices simultaneously from a single voice command.
In the video below, launching the projector automatically switches the amplifier's audio output from TV to projector.
No comments:
Post a Comment