Skip to content

Commit

Permalink
feat(input): added ability to pause/play/skip spotify tracks with the…
Browse files Browse the repository at this point in the history
… physical buttons
  • Loading branch information
SreeDan committed Jul 1, 2024
1 parent c149453 commit a36a847
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 39 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dotenv = "0.15.0"
image = "0.25.1"
serde_json = "1.0.118"
serde = { version = "1.0.203", features = ["derive"] }
once_cell = "1.19.0"
models = { path = "models" }
# graphics = { path = "graphics" }

Expand Down
193 changes: 154 additions & 39 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ use esp_idf_hal::{
gpio::{IOPin, PinDriver},
io::Read,
peripherals::Peripherals,
sys::esp_crt_bundle_attach,
sys::{esp_crt_bundle_attach, esp_get_free_heap_size, esp_get_minimum_free_heap_size},
};
use esp_idf_svc::{
eventloop::EspSystemEventLoop,
http::client::{Configuration as HttpConfig, EspHttpConnection},
nvs::EspDefaultNvsPartition,
wifi::{BlockingWifi, EspWifi},
};
use log::{error, info};
use models::CurrentlyPlaying;
use once_cell::sync::Lazy;
use serde_json::json;
use std::{
thread::{self, sleep},
Expand Down Expand Up @@ -55,76 +57,106 @@ fn main() {

wifi(&mut wifi_driver);

let check_playback = thread::Builder::new().stack_size(64 * 1024).spawn(|| {
loop {
let httpconnection = EspHttpConnection::new(&HttpConfig {
// use_global_ca_store: true,
crt_bundle_attach: Some(esp_crt_bundle_attach),
..Default::default()
})
.expect("Could not establish http connection");
static API_URL_ROOT: Lazy<String> = Lazy::new(|| {
include_str!("../api_url.txt")
.trim()
.replace("\n", "")
.to_string()
});

let mut httpclient = Client::wrap(httpconnection);
let url_root = include_str!("../api_url.txt").replace("\n", "");
let formatted_url = std::format!("{}/current_playback", url_root);
static AUTH_TOKEN: Lazy<String> = Lazy::new(|| {
include_str!("../auth_token.txt")
.trim()
.replace("\n", "")
.to_string()
});

let request = httpclient
.get(&formatted_url)
.expect("could not send current_playback request");
let check_playback = thread::Builder::new()
.stack_size(64 * 1024)
.spawn(|| {
loop {
let httpconnection = EspHttpConnection::new(&HttpConfig {
// use_global_ca_store: true,
crt_bundle_attach: Some(esp_crt_bundle_attach),
..Default::default()
})
.expect("Could not establish http connection");

let mut response = request.submit().expect("could not get response");
let mut httpclient = Client::wrap(httpconnection);
let formatted_url = std::format!("{}/current_playback", *API_URL_ROOT);

let mut playing_buf = vec![0u8; response.content_len().unwrap() as usize];
let mut image_buf = vec![0u8; 300 * 300];
let request = httpclient
.get(&formatted_url)
.expect("could not send current_playback request");

response.read_exact(&mut playing_buf).unwrap();
let mut response = request.submit().expect("could not get response");

let response_str = std::str::from_utf8(&playing_buf);
let mut playing_buf = vec![0u8; response.content_len().unwrap() as usize];
let mut image_buf = vec![0u8; 300 * 300];

let playing_json: Result<Option<CurrentlyPlaying>, serde_json::Error> =
serde_json::from_slice(&playing_buf);
response.read_exact(&mut playing_buf).unwrap();

if let Err(_) = playing_json {
Delay::new_default().delay_ms(5000);
continue;
}
let response_str = std::str::from_utf8(&playing_buf);

// println!("{:?}", response_str);
// println!("{:#?}", playing_json);
let playing_json: Result<Option<CurrentlyPlaying>, serde_json::Error> =
serde_json::from_slice(&playing_buf);

Delay::new_default().delay_ms(1000);
}
});
if let Err(_) = playing_json {
Delay::new_default().delay_ms(5000);
continue;
}

// println!("{:?}", response_str);
// println!("{:#?}", playing_json);

Delay::new_default().delay_ms(1000);
}
})
.unwrap();

loop {
if btn_pin1.is_high() && btn1_status == ButtonStatus::Low {
println!("Button 1 Pressed");
info!("Button 1 Pressed - Attempting to skip track");
// Using a button lock to make sure register one button input at a time
btn_lock = false;
btn1_status = ButtonStatus::High;

if !previous_track(&*API_URL_ROOT, &*AUTH_TOKEN) {
error!("could not go to previous track");
}
} else if btn_pin1.is_low() && !btn_lock {
btn_lock = true;
btn1_status = ButtonStatus::Low;
}

if btn_pin2.is_high() && btn2_status == ButtonStatus::Low {
println!("Button 2 Pressed");
info!("Button 2 Pressed - Attempting to toggle playback");
btn_lock = false;
btn2_status = ButtonStatus::High;

if !toggle_playback(&*API_URL_ROOT, &*AUTH_TOKEN) {
error!("could not toggle playback");
}
} else if btn_pin2.is_low() && !btn_lock {
btn_lock = true;
btn2_status = ButtonStatus::Low;
}

if btn_pin3.is_high() && btn3_status == ButtonStatus::Low {
println!("Button 3 Pressed");
info!("Button 3 Pressed - Attempting to skip track");
btn_lock = false;
btn3_status = ButtonStatus::High;

if !skip_track(&*API_URL_ROOT, &*AUTH_TOKEN) {
error!("could not go to next track");
}
} else if btn_pin3.is_low() && !btn_lock {
btn_lock = true;
btn3_status = ButtonStatus::Low;
}

print_memory_info();

thread::sleep(Duration::from_millis(100));
}
}
Expand All @@ -133,26 +165,109 @@ fn wifi(wifi_driver: &mut BlockingWifi<EspWifi>) {
// TODO: Make wifi work for enterprise networks -> I will need it for college wifi
let wifi_ssid = include_str!("../wifi_ssid.txt");
let wifi_password = include_str!("../wifi_password.txt");
let auth_token = include_str!("../auth_token.txt");

std::thread::sleep(Duration::from_millis(4000));
wifi_driver
.set_configuration(&embedded_svc::wifi::Configuration::Client(
embedded_svc::wifi::ClientConfiguration {
ssid: heapless::String::try_from(wifi_ssid).unwrap(),
password: heapless::String::try_from(wifi_password).unwrap(),
auth_method: esp_idf_svc::wifi::AuthMethod::WPA2Personal,
..Default::default()
},
))
.unwrap();

std::thread::sleep(Duration::from_millis(4000));
wifi_driver.start().unwrap();
wifi_driver.connect().unwrap();
let connection_info = wifi_driver.connect();

if let Err(e) = connection_info {
error!("something failed in connection info: {:?}", e);
}

std::thread::sleep(Duration::from_millis(4000));
info!("Waiting for wifi connection");
let waiting_info = wifi_driver.wait_netif_up();

println!("Waiting for wifi connection");
wifi_driver.wait_netif_up().unwrap();
if let Err(e) = waiting_info {
error!("Failed to connect to WiFi: {:?}", e);
}

println!(
info!(
"Connected to IP {:?}",
wifi_driver.wifi().sta_netif().get_ip_info()
);
}

fn toggle_playback(api_url_root: &String, auth_token: &String) -> bool {
let httpconnection = EspHttpConnection::new(&HttpConfig {
// use_global_ca_store: true,
crt_bundle_attach: Some(esp_crt_bundle_attach),
..Default::default()
})
.expect("Could not establish http connection");

let mut httpclient = Client::wrap(httpconnection);
let formatted_url = std::format!("{}/toggle_playback?auth_token={}", api_url_root, auth_token);
let request = httpclient
.get(&formatted_url)
.expect("could not send current_playback request");

let mut response = request.submit().expect("could not get response");
if response.status() != 200 {
error!("status code was {}", response.status());
}
return response.status() == 200;
}

fn skip_track(api_url_root: &String, auth_token: &String) -> bool {
let httpconnection = EspHttpConnection::new(&HttpConfig {
// use_global_ca_store: true,
crt_bundle_attach: Some(esp_crt_bundle_attach),
..Default::default()
})
.expect("Could not establish http connection");

let mut httpclient = Client::wrap(httpconnection);
let formatted_url = std::format!("{}/next_track?auth_token={}", api_url_root, auth_token);
let request = httpclient
.get(&formatted_url)
.expect("could not send next_track request");

let mut response = request.submit().expect("could not get response");
if response.status() != 200 {
error!("status code was {}", response.status());
}
return response.status() == 200;
}

fn previous_track(api_url_root: &String, auth_token: &String) -> bool {
let httpconnection = EspHttpConnection::new(&HttpConfig {
// use_global_ca_store: true,
crt_bundle_attach: Some(esp_crt_bundle_attach),
..Default::default()
})
.expect("Could not establish http connection");

let mut httpclient = Client::wrap(httpconnection);
let formatted_url = std::format!("{}/previous_track?auth_token={}", api_url_root, auth_token);
let request = httpclient
.get(&formatted_url)
.expect("could not send previous_track request");

let mut response = request.submit().expect("could not get response");
if response.status() != 200 {
error!("status code was {}", response.status());
}
return response.status() == 200;
}

fn print_memory_info() {
println!("Total free heap size: {} bytes", unsafe {
esp_get_free_heap_size()
});
println!("Minimum free heap size ever: {} bytes", unsafe {
esp_get_minimum_free_heap_size()
});
}

0 comments on commit a36a847

Please sign in to comment.