Today some lasercutting for Home Assistant Spotify RFID

see:

Lasercutting a case and the playlist selectors.

Close-up RFID stickers I’m using.

Below is a test with different methods.
I like reading the booklets, so a CD i cool, and I don’t need a CD player.
(The RFID tag is in the case)
The little cards are for bought audio files I don’t have a physical CD for.

Wooden case with RFID reader being powered by external powerbank

What am I gonna do?
Cube as I had? Wooden playlist selectors as in above movies?
The cards I’ve printed?
Maybe a small record player with an RFID reader inside?

3D printed like this? https://makerworld.com/en/models/66671
UPDATE: 20240327 – Little Record I 3D printed with little groves.

Home Assistant code for Playlist and Album automations
(B.t.w. The method is still using an Arduino and MQTT topics, as mentioned before)

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# ALBUM PLAYER
alias: SpotifyAlbum
description: ""
trigger:
- platform: mqtt
topic: spotify/rfid/id
condition:
- condition: template
value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
- service: media_player.play_media
target:
entity_id: media_player.spotify_fashice
data:
media_content_type: album
media_content_id: spotify:album:{{ playlistkeys.get(trigger.payload) }}
mode: single
variables:
playlistkeys:
"71719674": 20TANs4iXVeLp387zjgmec
"71260666": 5325ECcBhnIysoqyENGCYi
"71457530": 7wyOeD9HcUuMFMO8pTflap
# ALBUM PLAYER alias: SpotifyAlbum description: "" trigger: - platform: mqtt topic: spotify/rfid/id condition: - condition: template value_template: "{{ trigger.payload in playlistkeys.keys() }}" action: - service: media_player.play_media target: entity_id: media_player.spotify_fashice data: media_content_type: album media_content_id: spotify:album:{{ playlistkeys.get(trigger.payload) }} mode: single variables: playlistkeys: "71719674": 20TANs4iXVeLp387zjgmec "71260666": 5325ECcBhnIysoqyENGCYi "71457530": 7wyOeD9HcUuMFMO8pTflap
# ALBUM PLAYER
alias: SpotifyAlbum
description: ""
trigger:
  - platform: mqtt
    topic: spotify/rfid/id
condition:
  - condition: template
    value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
  - service: media_player.play_media
    target:
      entity_id: media_player.spotify_fashice
    data:
      media_content_type: album
      media_content_id: spotify:album:{{ playlistkeys.get(trigger.payload) }}
mode: single
variables:
  playlistkeys:
    "71719674": 20TANs4iXVeLp387zjgmec
    "71260666": 5325ECcBhnIysoqyENGCYi
    "71457530": 7wyOeD9HcUuMFMO8pTflap
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# PLAYLIST PLAYER
alias: SpotifyCube
description: ""
trigger:
- platform: mqtt
topic: spotify/rfid/id
condition:
- condition: template
value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
- service: media_player.play_media
target:
entity_id: media_player.spotify_fashice
data:
media_content_type: playlist
media_content_id: spotify:user:spotify:playlist:{{ playlistkeys.get(trigger.payload) }}
variables:
playlistkeys:
"69229050": 0SOay3RkjojjevrF5lHMON
"69491194": 5f8w3UHlD9Ozz6Y4VHs6kF
"69753338": 0bJvpsn0TDZwIDUjz4d75S
"70015482": 37i9dQZF1DX9HwI3Crikcm
"70277626": 37i9dQZF1EQmK1rjZuPGDt
"70539770": 2KeRLMmGMxI5UgzE7m0iCp
# PLAYLIST PLAYER alias: SpotifyCube description: "" trigger: - platform: mqtt topic: spotify/rfid/id condition: - condition: template value_template: "{{ trigger.payload in playlistkeys.keys() }}" action: - service: media_player.play_media target: entity_id: media_player.spotify_fashice data: media_content_type: playlist media_content_id: spotify:user:spotify:playlist:{{ playlistkeys.get(trigger.payload) }} variables: playlistkeys: "69229050": 0SOay3RkjojjevrF5lHMON "69491194": 5f8w3UHlD9Ozz6Y4VHs6kF "69753338": 0bJvpsn0TDZwIDUjz4d75S "70015482": 37i9dQZF1DX9HwI3Crikcm "70277626": 37i9dQZF1EQmK1rjZuPGDt "70539770": 2KeRLMmGMxI5UgzE7m0iCp
# PLAYLIST PLAYER
alias: SpotifyCube
description: ""
trigger:
  - platform: mqtt
    topic: spotify/rfid/id
condition:
  - condition: template
    value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
  - service: media_player.play_media
    target:
      entity_id: media_player.spotify_fashice
    data:
      media_content_type: playlist
      media_content_id: spotify:user:spotify:playlist:{{ playlistkeys.get(trigger.payload) }}
variables:
  playlistkeys:
    "69229050": 0SOay3RkjojjevrF5lHMON
    "69491194": 5f8w3UHlD9Ozz6Y4VHs6kF
    "69753338": 0bJvpsn0TDZwIDUjz4d75S
    "70015482": 37i9dQZF1DX9HwI3Crikcm
    "70277626": 37i9dQZF1EQmK1rjZuPGDt
    "70539770": 2KeRLMmGMxI5UgzE7m0iCp

In the past, Aloha and I made a simple solution like this using barcodes in < 2000s.
Due to the many obscure recordings I have, I am thinking about creating something like this for Picore player and my local Squeezebox server.

Logitech Squeezebox / Media Server Solution

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
alias: squeezealbumplay
description: ""
trigger:
- platform: mqtt
topic: spotify/rfid/id
condition:
- condition: template
value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
- service: squeezebox.call_method
target:
entity_id: media_player.squeezebox
data:
command: playlist
parameters:
- play
- "{{ playlistkeys.get(trigger.payload) }}"
mode: single
variables:
playlistkeys:
"71719674": /tank/celtic/Celtic/M/Martyn Bennett/Bothy Culture/
"71719675": /tank/celtic/Celtic/D/Davy Spillane/Atlantic Bridge/
"2159056458": /tank/celtic/Celtic/M/Michael McGoldrick/Arc/
alias: squeezealbumplay description: "" trigger: - platform: mqtt topic: spotify/rfid/id condition: - condition: template value_template: "{{ trigger.payload in playlistkeys.keys() }}" action: - service: squeezebox.call_method target: entity_id: media_player.squeezebox data: command: playlist parameters: - play - "{{ playlistkeys.get(trigger.payload) }}" mode: single variables: playlistkeys: "71719674": /tank/celtic/Celtic/M/Martyn Bennett/Bothy Culture/ "71719675": /tank/celtic/Celtic/D/Davy Spillane/Atlantic Bridge/ "2159056458": /tank/celtic/Celtic/M/Michael McGoldrick/Arc/
alias: squeezealbumplay
description: ""
trigger:
  - platform: mqtt
    topic: spotify/rfid/id
condition:
  - condition: template
    value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
  - service: squeezebox.call_method
    target:
      entity_id: media_player.squeezebox
    data:
      command: playlist
      parameters:
        - play
        - "{{ playlistkeys.get(trigger.payload) }}"
mode: single
variables:
  playlistkeys:
    "71719674": /tank/celtic/Celtic/M/Martyn Bennett/Bothy Culture/
    "71719675": /tank/celtic/Celtic/D/Davy Spillane/Atlantic Bridge/
    "2159056458": /tank/celtic/Celtic/M/Michael McGoldrick/Arc/

Several things in progress, help me if you can.

I used MCE to control some Windows VMs and programs running in it in the past. (Below link and a web interface engine which on the backend converted BWW/BMW (bagpipe music files) to PDF automated comes to mind)

Now, I implemented this:

https://iotlink.gitlab.io/

Controlling a Windows VM using MQTT, very nice!
(Use HA mqtt or mosquitto_pub in bash)

Question: anyone got a better solution to control programs within a VM? Let me know.

Next:

I’m creating a new case for my Wemos, LCD16x2, button, Led, Buzzer project (see other post)

I’m redesigning my previous case in blender.

But I really miss something like a generator function for different cases, like the one I made using Openscad.
Question: Anyone know a tool/add-on to generate cases?
I used a model of a wemos to get the usb connector/screw holes in place.

My spotify rfid case I will create using wooden lasercut cutouts.
https://www.henriaanstoot.nl/2024/03/06/revisiting-the-spotify-cube/

Next one:

In the past, I’ve controlled some blender lights using python and MQTT. But now I’m trying to control it using DMX.

Example of lighting in our living using mock-up couch and tables.

I found a cool add-on called Blender-DMX.
(B.t.w. wled can also use DMX)

Looks cool but, can I make a floorplan with this?

Blender add-on configuration

In Home Assistant I used a HACS add-on called : Art-net LED Lighting for DMX

Configuration can be done in configuration.yaml

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
light:
- platform: artnet_led
host: BLENDERHOSTIP # IP of Art-Net Node
max_fps: 25
refresh_every: 0 # Resend values if no fades are running every x seconds, 0 disables automatic refresh
node_type: artnet-direct # Which protocol to use
universes: # Support for multiple universes
1: # .Nr of Universe (see configuration of your Art-Net Node)
send_partial_universe: True # Only send the universe which contains data
devices:
- channel: 1 # first channel of dmx dimmer
name: dmx_dimmer_rgbw # name
type: rgbw # type
transition: 1 # default duration of fades in sec.
channel_size: 8bit # width of the channel sent to DMX device, default "8bit", "16bit", "24bit" and "32bit"
channel_setup: Wrgb # This is the magic to get colors correct
light: - platform: artnet_led host: BLENDERHOSTIP # IP of Art-Net Node max_fps: 25 refresh_every: 0 # Resend values if no fades are running every x seconds, 0 disables automatic refresh node_type: artnet-direct # Which protocol to use universes: # Support for multiple universes 1: # .Nr of Universe (see configuration of your Art-Net Node) send_partial_universe: True # Only send the universe which contains data devices: - channel: 1 # first channel of dmx dimmer name: dmx_dimmer_rgbw # name type: rgbw # type transition: 1 # default duration of fades in sec. channel_size: 8bit # width of the channel sent to DMX device, default "8bit", "16bit", "24bit" and "32bit" channel_setup: Wrgb # This is the magic to get colors correct
light:
- platform: artnet_led
  host: BLENDERHOSTIP                   # IP of Art-Net Node
  max_fps: 25                           
  refresh_every: 0                      # Resend values if no fades are running every x seconds, 0 disables automatic refresh
  node_type: artnet-direct              # Which protocol to use
  universes:                            # Support for multiple universes
    1:                                  # .Nr of Universe (see configuration of your Art-Net Node)
      send_partial_universe: True       # Only send the universe which contains data
      devices:
        - channel: 1                    # first channel of dmx dimmer
          name: dmx_dimmer_rgbw         # name
          type: rgbw                    # type
          transition: 1                 # default duration of fades in sec. 
          channel_size: 8bit            # width of the channel sent to DMX device, default "8bit", "16bit", "24bit" and "32bit" 
          channel_setup: Wrgb           # This is the magic to get colors correct

It works, but I’m not happy, anyone got a better solution?

And I have to check out GDTF profiles for fixtures.

At a later stage I’m going to 3d print a white floorplan about 1cm high, with LEDs and buttons. A floorplan you can hang on your wall.

Revisiting the Spotify Cube

In the past I posted about my genre selector for Spotify using a cube.

UPDATE: 20240501 below – using esphome

Most was done using NodeRed and a python script.

Now, I’ve moved it to Home Assistant using a single automation.
(Maybe the Arduino sketch can be made with Esphome also.
But I don’t have time for that)
It still uses the Arduino sketch as before, which uses Mqtt to post the RFID code to Mosquitto.

My new Home Assistant automation

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
alias: SpotifyCube
description: ""
trigger:
- platform: mqtt
topic: spotify/rfid/id
condition:
- condition: template
value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
- service: media_player.play_media
target:
entity_id: media_player.spotify_fash
data:
media_content_type: playlist
media_content_id: spotify:user:spotify:playlist:{{ playlistkeys.get(trigger.payload) }}
variables:
playlistkeys:
"70539770": 2KeRLMmGMxI5UgzE7m0iCx
"70277626": 37i9dQZF1EQmK1rjZuPGDx
"69229050": 0SOay3RkjojjevrF5lHMOx
"70015482": 37i9dQZF1DX9HwI3Crikcx
"69753338": 0bJvpsn0TDZwIDUjz4d75x
"69491194": 5f8w3UHlD9Ozz6Y4VHs6kx
alias: SpotifyCube description: "" trigger: - platform: mqtt topic: spotify/rfid/id condition: - condition: template value_template: "{{ trigger.payload in playlistkeys.keys() }}" action: - service: media_player.play_media target: entity_id: media_player.spotify_fash data: media_content_type: playlist media_content_id: spotify:user:spotify:playlist:{{ playlistkeys.get(trigger.payload) }} variables: playlistkeys: "70539770": 2KeRLMmGMxI5UgzE7m0iCx "70277626": 37i9dQZF1EQmK1rjZuPGDx "69229050": 0SOay3RkjojjevrF5lHMOx "70015482": 37i9dQZF1DX9HwI3Crikcx "69753338": 0bJvpsn0TDZwIDUjz4d75x "69491194": 5f8w3UHlD9Ozz6Y4VHs6kx
alias: SpotifyCube
description: ""
trigger:
  - platform: mqtt
    topic: spotify/rfid/id
condition:
  - condition: template
    value_template: "{{ trigger.payload in playlistkeys.keys() }}"
action:
  - service: media_player.play_media
    target:
      entity_id: media_player.spotify_fash
    data:
      media_content_type: playlist
      media_content_id: spotify:user:spotify:playlist:{{ playlistkeys.get(trigger.payload) }}
variables:
  playlistkeys:
    "70539770": 2KeRLMmGMxI5UgzE7m0iCx
    "70277626": 37i9dQZF1EQmK1rjZuPGDx
    "69229050": 0SOay3RkjojjevrF5lHMOx
    "70015482": 37i9dQZF1DX9HwI3Crikcx
    "69753338": 0bJvpsn0TDZwIDUjz4d75x
    "69491194": 5f8w3UHlD9Ozz6Y4VHs6kx

Some notes about above script:

  • The MQTT topic is configured in the sketch below
  • The playlist keys are at the bottom
    “RFIDID”: playliststring as can be found in web spotify

Pasted link

Arduino Code

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <PubSubClient.h>
#define SS_PIN 15
#define RST_PIN 0
MFRC522 mfrc522(SS_PIN, RST_PIN);
unsigned long cardId = 0;
WiFiClient net;
PubSubClient client(net);
const char* mqtt_server = "IPMQTTBROKER";
const char* ssid = "MYSSID";
const char* password = "MYWIFIPASSWORD";
void setup() {
Serial.begin(115200);
SPI.begin();
mfrc522.PCD_Init();
WiFi.mode(WIFI_AP_STA);
WiFi.begin(ssid, password);
client.setServer(mqtt_server, 1883);
delay(100);
client.setCallback(callback);
delay(100);
client.subscribe("spotify/rfid/in/#");
}
void reconnect() {
while (WiFi.waitForConnectResult() != WL_CONNECTED) {
}
while (!client.connected()) {
String clientId = "NodeMCUClient-";
clientId += String(random(0xffff), HEX);
if (!client.connect(clientId.c_str(), "rfidclient", "...")) {
Serial.print("failed, rc=");
Serial.print(client.state());
delay(5000);
}
}
client.subscribe("spotify/rfid/in/#");
}
void callback(char* topic, byte* payload, unsigned int length) {
String topicStr = topic;
byte value = atoi((char*)payload);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
if (!mfrc522.PICC_IsNewCardPresent()) {
return;
}
if (!mfrc522.PICC_ReadCardSerial()) {
return;
}
cardId = getCardId();
char buffer[10];
sprintf(buffer, "%lu", cardId);
client.publish("spotify/rfid/id", buffer);
uint8_t control = 0x00;
do {
control = 0;
for (int i = 0; i < 3; i++) {
if (!mfrc522.PICC_IsNewCardPresent()) {
if (mfrc522.PICC_ReadCardSerial()) {
control |= 0x16;
}
if (mfrc522.PICC_ReadCardSerial()) {
control |= 0x16;
}
control += 0x1;
}
control += 0x4;
}
delay(0);
} while (control == 13 || control == 14);
reconnect();
client.publish("spotify/rfid/id", "0");
delay(500);
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}
unsigned long getCardId() {
byte readCard[4];
for (int i = 0; i < 4; i++) {
readCard[i] = mfrc522.uid.uidByte[i];
}
return (unsigned long)readCard[0] << 24
| (unsigned long)readCard[1] << 16
| (unsigned long)readCard[2] << 8
| (unsigned long)readCard[3];
}
#include <Arduino.h> #include <SPI.h> #include <MFRC522.h> #include <ESP8266WiFi.h> #include <WiFiClient.h> #include <PubSubClient.h> #define SS_PIN 15 #define RST_PIN 0 MFRC522 mfrc522(SS_PIN, RST_PIN); unsigned long cardId = 0; WiFiClient net; PubSubClient client(net); const char* mqtt_server = "IPMQTTBROKER"; const char* ssid = "MYSSID"; const char* password = "MYWIFIPASSWORD"; void setup() { Serial.begin(115200); SPI.begin(); mfrc522.PCD_Init(); WiFi.mode(WIFI_AP_STA); WiFi.begin(ssid, password); client.setServer(mqtt_server, 1883); delay(100); client.setCallback(callback); delay(100); client.subscribe("spotify/rfid/in/#"); } void reconnect() { while (WiFi.waitForConnectResult() != WL_CONNECTED) { } while (!client.connected()) { String clientId = "NodeMCUClient-"; clientId += String(random(0xffff), HEX); if (!client.connect(clientId.c_str(), "rfidclient", "...")) { Serial.print("failed, rc="); Serial.print(client.state()); delay(5000); } } client.subscribe("spotify/rfid/in/#"); } void callback(char* topic, byte* payload, unsigned int length) { String topicStr = topic; byte value = atoi((char*)payload); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); if (!mfrc522.PICC_IsNewCardPresent()) { return; } if (!mfrc522.PICC_ReadCardSerial()) { return; } cardId = getCardId(); char buffer[10]; sprintf(buffer, "%lu", cardId); client.publish("spotify/rfid/id", buffer); uint8_t control = 0x00; do { control = 0; for (int i = 0; i < 3; i++) { if (!mfrc522.PICC_IsNewCardPresent()) { if (mfrc522.PICC_ReadCardSerial()) { control |= 0x16; } if (mfrc522.PICC_ReadCardSerial()) { control |= 0x16; } control += 0x1; } control += 0x4; } delay(0); } while (control == 13 || control == 14); reconnect(); client.publish("spotify/rfid/id", "0"); delay(500); mfrc522.PICC_HaltA(); mfrc522.PCD_StopCrypto1(); } unsigned long getCardId() { byte readCard[4]; for (int i = 0; i < 4; i++) { readCard[i] = mfrc522.uid.uidByte[i]; } return (unsigned long)readCard[0] << 24 | (unsigned long)readCard[1] << 16 | (unsigned long)readCard[2] << 8 | (unsigned long)readCard[3]; }
#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <PubSubClient.h>

#define SS_PIN 15
#define RST_PIN 0

MFRC522 mfrc522(SS_PIN, RST_PIN);
unsigned long cardId = 0;

WiFiClient net;
PubSubClient client(net);
const char* mqtt_server = "IPMQTTBROKER";
const char* ssid = "MYSSID";
const char* password = "MYWIFIPASSWORD";

void setup() {

  Serial.begin(115200);
  SPI.begin();
  mfrc522.PCD_Init();
  WiFi.mode(WIFI_AP_STA);
  WiFi.begin(ssid, password);

  client.setServer(mqtt_server, 1883);
     delay(100);
    client.setCallback(callback);
      delay(100);
    client.subscribe("spotify/rfid/in/#");
}

void reconnect() {
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
  }

  while (!client.connected()) {
    String clientId = "NodeMCUClient-";
    clientId += String(random(0xffff), HEX);

    if (!client.connect(clientId.c_str(), "rfidclient", "...")) {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      delay(5000);
    }

  }
  client.subscribe("spotify/rfid/in/#");
}

void callback(char* topic, byte* payload, unsigned int length) {
    String topicStr = topic;
      byte value = atoi((char*)payload);
}

void loop() {
    if (!client.connected()) {
    reconnect();
  }
  client.loop();

  if (!mfrc522.PICC_IsNewCardPresent()) {
    return;
  }

  if (!mfrc522.PICC_ReadCardSerial()) {
    return;
  }

  cardId = getCardId();
  char buffer[10];
  sprintf(buffer, "%lu", cardId);
  client.publish("spotify/rfid/id", buffer);

  uint8_t control = 0x00;
  do {
    control = 0;
    for (int i = 0; i < 3; i++) {
      if (!mfrc522.PICC_IsNewCardPresent()) {
        if (mfrc522.PICC_ReadCardSerial()) {
          control |= 0x16;
        }
        if (mfrc522.PICC_ReadCardSerial()) {
          control |= 0x16;
        }
        control += 0x1;
      }
      control += 0x4;
    }

    delay(0);
  } while (control == 13 || control == 14);

  reconnect();
  client.publish("spotify/rfid/id", "0");
  delay(500);

  mfrc522.PICC_HaltA();
  mfrc522.PCD_StopCrypto1();
}

unsigned long getCardId() {
  byte readCard[4];
  for (int i = 0; i < 4; i++) {
    readCard[i] = mfrc522.uid.uidByte[i];
  }

  return (unsigned long)readCard[0] << 24
    | (unsigned long)readCard[1] << 16
    | (unsigned long)readCard[2] << 8
    | (unsigned long)readCard[3];
}

ESPHOME Config same as above

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
esphome:
name: rfidtag
friendly_name: rfidtag
esp8266:
board: d1_mini
mqtt:
broker: 192.168.1.1
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "xxxxxxxxxxxxxxx="
ota:
password: "xxxxxxxxxxxxxx"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Rfidtag Fallback Hotspot"
password: "xxxxxxxxxxx"
captive_portal:
spi:
clk_pin: D5
miso_pin: D6
mosi_pin: D7
rc522_spi:
cs_pin: D8
update_interval: 1s
on_tag:
then:
- mqtt.publish:
topic: spotify/rfid/id
payload: !lambda 'return x;'
on_tag_removed:
then:
- mqtt.publish:
topic: spotify/rfid/idremoved
payload: !lambda 'return x;'
esphome: name: rfidtag friendly_name: rfidtag esp8266: board: d1_mini mqtt: broker: 192.168.1.1 # Enable logging logger: # Enable Home Assistant API api: encryption: key: "xxxxxxxxxxxxxxx=" ota: password: "xxxxxxxxxxxxxx" wifi: ssid: !secret wifi_ssid password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "Rfidtag Fallback Hotspot" password: "xxxxxxxxxxx" captive_portal: spi: clk_pin: D5 miso_pin: D6 mosi_pin: D7 rc522_spi: cs_pin: D8 update_interval: 1s on_tag: then: - mqtt.publish: topic: spotify/rfid/id payload: !lambda 'return x;' on_tag_removed: then: - mqtt.publish: topic: spotify/rfid/idremoved payload: !lambda 'return x;'
esphome:
  name: rfidtag
  friendly_name: rfidtag

esp8266:
  board: d1_mini

mqtt:
  broker: 192.168.1.1

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxxxxxxxxxxxx="

ota:
  password: "xxxxxxxxxxxxxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Rfidtag Fallback Hotspot"
    password: "xxxxxxxxxxx"

captive_portal:
    
spi:
  clk_pin: D5
  miso_pin: D6
  mosi_pin: D7
rc522_spi:
  cs_pin: D8
  update_interval: 1s
  on_tag:
    then:
      - mqtt.publish:
          topic: spotify/rfid/id
          payload: !lambda 'return x;'
  on_tag_removed:
    then:
      - mqtt.publish:
          topic: spotify/rfid/idremoved
          payload: !lambda 'return x;'

Adding a VGA terminal to my 6502

Using a LilyGo TTGO ESP32 VGA32, I’m connecting my breadboard 6502 to a serial vga terminal with its own keyboard.

Due to a lot of moving around, new places, new homes I dumped a lot of terminal hardware.
Also are those old terminals too big and use too much power.

I’m going to use this DIY screen.
https://www.henriaanstoot.nl/2021/03/24/broken-or-slow-laptop-screen-still-works/

Using the Libraries from Fabrizio Di Vittorio, named FabGL, you can transform this device into a dumb terminal, game device, VIC-20, a 8086 pc and more.
There are even some projects to turn this into a C64.

But the main thing I want to do: A simple terminal.
(I probably revisit the other options again at a later stage)

My Wozmon bios has bare minimum support for serial communication, so i have to do some bitbanging.
(6502 is using a 6551 ACIA)

Sound from the ESP32 VGA board.

  • Chipset: TTGO Micro32 (ESP32 240Mhz dual core processor)
  • Flash memory: 4MB
  • SRAM: 520KB
  • Built-in Bluetooth
  • Built-in Wi-Fi
  • Supply voltage: 3.3V DC or 5V DC
  • GPIO voltage: 3.3V*
  • USB to serial converter: CP2102 or CH9102F (drivers)
  • VGA connection
  • PS/2: keyboard connection
  • PS/2: mouse connection
  • Built-in Li-ion/Li-Po battery charging circuit: TP4054 chip can charge up to 500mA

My google-fu is still strong (and Tunepal ramblings)

Google-Fu : (informal) Skill in using search engines (especially Google) to quickly find useful information on the Internet.

I was thinking of a famous piece of music, but what was it?

Whistling it, while using Shazam or Tunepal, didn’t work.

So I googled “well known classical part repeats sped up and transposes”

The second link was a Reddit link named : “Need help finding a song that starts very slow and builds to be frantic!”

First YT link in there: In the Hall of the Mountain King (Peer Gynt) by Edvard Grieg

Epic tune!

“In the Hall of the Mountain King” is a piece of orchestral music composed by Edvard Grieg in 1875 as incidental music for the sixth scene of act 2 in Henrik Ibsen’s 1867 play Peer Gynt. It was originally part of Opus 23 but was later extracted as the final piece of Peer Gynt, Suite No. 1, Op. 46.

I’ve used Tunepal many times, it’s great for folkies!

Sometimes it works also on classical pieces, because they were arranged into folk music.

https://tunepal.org/

I bought the Android app, because I liked it so much.

Tunepal is a search-by-playing search engine for traditional Irish, Welsh, Scottish, Breton, American marching band and Canadian tunes.
By playing a 12-second extract from a traditional tune on an instrument such as the flute or fiddle, you can:

  • Retrieve score matches from a database of over 24,000 music scores
  • View and playback, share and download the score
  • Find and play other recordings of the tune from a collection of over 30 million recordings

Tunepal is a search-by-playing search engine for traditional Irish, Welsh, Scottish, Breton, American marching band and Canadian tunes.

On the Record page, click the Tunepal logo or tap the screen if your computer has a touch screen. Start playing straight away. Don’t wait for the countdown to complete. Tunepal works best if there is no silence at the start of the recording.

Tunepal works best with “legato” style instruments such as the tin-whistle, flute, concertina, accordion, pipes and fiddle. It doesn’t work very well with “plucked string” instruments such as the banjo and harp.

To find a tune using Tunepal, first make sure you have a PC microphone connected to your computer or use the phone app

If your instrument uses a different “fundamental note” to the usual D (for example you are playing a C flute) or you are playing a tune in an unusual key, then you can adjust the transcription algorithm by choosing a different “fundamental” from the settings page.

You can filter the search results from the settings page to limit searches to certain tunebooks or time signatures.

Home Assistant and Harmony Hub Scripts

I’ve got a Logitech Harmony Hub to IR control devices.

When adding this to your home assistant, you initially only get the Activities.

But I want to add single button presses.
I was going to re-visit my IR remote project.
(The IR blaster and the IR wemos, see previous posts)

Thanks to Duncan I had a second look at this solution

Check the files using the file editor.
Search for /homeassistant/harmony_xxxxxxxx.conf

It will show the Hub devices and their capabilities.

Short example below

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
{
"Activities": {
"-1": "PowerOff",
"42652474": "Watch TV",
"43054933": "Squeeze",
"43054959": "Watch Netflix",
"43072690": "Switch",
"43073688": "Spotify",
"43557114": "Kodi",
"49494467": "Tv on",
"52476284": "Boxon",
"52476285": "Boxoff"
},
"Devices": {
"Google Chromecast": {
"commands": [],
"id": "67134460"
},
"LG TV": {
"commands": [
"PowerOff",
"PowerOn",
"PowerToggle",
".",
"-",
"0",
-----------8<----------------------------- SNIP SNAP
"SportsMode"
],
"id": "67134658"
},
"Onkyo AV Receiver": {
"commands": [
"PowerOff",
"PowerOn",
-----------8<----------------------------- SNIP SNAP
"VolumeLevelUp5Step"
],
"id": "67134459"
},
"arcadyan DVR": {
"commands": [
"PowerToggle",
-----------8<----------------------------- SNIP SNAP
"TV"
],
"id": "67134775"
}
}
}
{ "Activities": { "-1": "PowerOff", "42652474": "Watch TV", "43054933": "Squeeze", "43054959": "Watch Netflix", "43072690": "Switch", "43073688": "Spotify", "43557114": "Kodi", "49494467": "Tv on", "52476284": "Boxon", "52476285": "Boxoff" }, "Devices": { "Google Chromecast": { "commands": [], "id": "67134460" }, "LG TV": { "commands": [ "PowerOff", "PowerOn", "PowerToggle", ".", "-", "0", -----------8<----------------------------- SNIP SNAP "SportsMode" ], "id": "67134658" }, "Onkyo AV Receiver": { "commands": [ "PowerOff", "PowerOn", -----------8<----------------------------- SNIP SNAP "VolumeLevelUp5Step" ], "id": "67134459" }, "arcadyan DVR": { "commands": [ "PowerToggle", -----------8<----------------------------- SNIP SNAP "TV" ], "id": "67134775" } } }
{
    "Activities": {
        "-1": "PowerOff",
        "42652474": "Watch TV",
        "43054933": "Squeeze",
        "43054959": "Watch Netflix",
        "43072690": "Switch",
        "43073688": "Spotify",
        "43557114": "Kodi",
        "49494467": "Tv on",
        "52476284": "Boxon",
        "52476285": "Boxoff"
    },
    "Devices": {
        "Google Chromecast": {
            "commands": [],
            "id": "67134460"
        },
        "LG TV": {
            "commands": [
                "PowerOff",
                "PowerOn",
                "PowerToggle",
                ".",
                "-",
                "0",
 -----------8<----------------------------- SNIP SNAP
               "SportsMode"
            ],
            "id": "67134658"
        },
        "Onkyo AV Receiver": {
            "commands": [
                "PowerOff",
                "PowerOn",
 -----------8<----------------------------- SNIP SNAP
                "VolumeLevelUp5Step"
            ],
            "id": "67134459"
        },
        "arcadyan DVR": {
            "commands": [
                "PowerToggle",
 -----------8<----------------------------- SNIP SNAP
                "TV"
            ],
            "id": "67134775"
        }
    }
}

Lets create a Script for this

See the IDs above to refer to commands below.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
alias: Netflix
sequence:
- service: remote.send_command
target:
device_id: 7e82a825decabedbc98a0b5ce2ac5d78
data:
num_repeats: 1
delay_secs: 0.4
hold_secs: 1.2
command: PowerOn
device: "67134658"
- service: remote.send_command
metadata: {}
data:
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: InputStb/Dvr
device: "67134459"
target:
device_id: 7e82a825decabedbc98a0b5ce2ac5d78
- service: remote.send_command
metadata: {}
data:
num_repeats: 1
delay_secs: 0.4
hold_secs: 0
command: Netflix
device: "67134658"
target:
device_id: 7e82a825decabedbc98a0b5ce2ac5d78
mode: single
alias: Netflix sequence: - service: remote.send_command target: device_id: 7e82a825decabedbc98a0b5ce2ac5d78 data: num_repeats: 1 delay_secs: 0.4 hold_secs: 1.2 command: PowerOn device: "67134658" - service: remote.send_command metadata: {} data: num_repeats: 1 delay_secs: 0.4 hold_secs: 0 command: InputStb/Dvr device: "67134459" target: device_id: 7e82a825decabedbc98a0b5ce2ac5d78 - service: remote.send_command metadata: {} data: num_repeats: 1 delay_secs: 0.4 hold_secs: 0 command: Netflix device: "67134658" target: device_id: 7e82a825decabedbc98a0b5ce2ac5d78 mode: single
alias: Netflix
sequence:
  - service: remote.send_command
    target:
      device_id: 7e82a825decabedbc98a0b5ce2ac5d78
    data:
      num_repeats: 1
      delay_secs: 0.4
      hold_secs: 1.2
      command: PowerOn
      device: "67134658"
  - service: remote.send_command
    metadata: {}
    data:
      num_repeats: 1
      delay_secs: 0.4
      hold_secs: 0
      command: InputStb/Dvr
      device: "67134459"
    target:
      device_id: 7e82a825decabedbc98a0b5ce2ac5d78
  - service: remote.send_command
    metadata: {}
    data:
      num_repeats: 1
      delay_secs: 0.4
      hold_secs: 0
      command: Netflix
      device: "67134658"
    target:
      device_id: 7e82a825decabedbc98a0b5ce2ac5d78
mode: single

Add these scripts to buttons

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
type: entities
entities:
- script.allmediaoff
- script.netflix
- script.tvon
type: entities entities: - script.allmediaoff - script.netflix - script.tvon
type: entities
entities:
  - script.allmediaoff
  - script.netflix
  - script.tvon

Some nice google matching tests.

Step one, change your main DNS resolver to that one from your provider or 1.1.1.1 (cloudflare)

Goto https://www.hawking.org.uk/ in your browser

Check youtube, facebook and more.

Set you DNS to 8.8.8.8 (google)

Do the same (even in a incognito browser).

I haven’t seen a Hawking recommendation on YT for ages, I just checked a website with information on another machine.
And my YT stream showed …. Stephen Hawking!

Kodi camera stream push playlist

Below is a solution when you want to stream IP camera’s in Kodi/Libreelec .

You can push these commands using Nodered, Bash script or whatever.

First make some camera scripts in your profile directory.

Examples:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# Kodi on Linux/Raspberry
# Place a file cam1.m3u in .kodi/userdata/profiles/(kodiprofile)/playlists/video/
rtsp://admin:secretpass@192.168.1.123:88/videoMain
#and another one in cam2.m3u (another example mjpeg example)
http://192.168.1.124:8000/stream.mjpg
#For windows it is in
C:\Users\(Username)\AppData\Roaming\Kodi\userdata\profiles\(kodiprofile)\playlists\video
# Kodi on Linux/Raspberry # Place a file cam1.m3u in .kodi/userdata/profiles/(kodiprofile)/playlists/video/ rtsp://admin:secretpass@192.168.1.123:88/videoMain #and another one in cam2.m3u (another example mjpeg example) http://192.168.1.124:8000/stream.mjpg #For windows it is in C:\Users\(Username)\AppData\Roaming\Kodi\userdata\profiles\(kodiprofile)\playlists\video
# Kodi on Linux/Raspberry
# Place a file cam1.m3u in .kodi/userdata/profiles/(kodiprofile)/playlists/video/
rtsp://admin:secretpass@192.168.1.123:88/videoMain

#and another one in cam2.m3u (another example mjpeg example)
http://192.168.1.124:8000/stream.mjpg

#For windows it is in
C:\Users\(Username)\AppData\Roaming\Kodi\userdata\profiles\(kodiprofile)\playlists\video

Enable http access in Kodi and run the playlist using curl

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam2.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam2.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam2.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc

A bash loop script

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
while true; do
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam1.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc
sleep 10
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam2.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc
sleep 5
done
while true; do curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam1.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc sleep 10 curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam2.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc sleep 5 done
while true; do
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam1.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc
sleep 10
curl -i -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","method":"Player.Open","params":{"options":{"shuffled":false,"repeat":"off"},"item":{"file":"special://profile/playlists/video/cam2.m3u"}},"id":"1"}' http://KODISERVERIP:8080/jsonrpc
sleep 5
done

Three channel mixer for ay-3-8910 is almost done.

At the back the 8 pin single channel lm368 amplifier.
At the front the 3 channel setup.
I still have to tweak the resistors, and potmeters.
Then I can make a permanent PCB, and figure out the connections to the 6502.

At the moment, the Arduino Nano is playing some real sound samples by using the registers of the sound chip.
The music is being played by sending the register dumps directly to the chip.

Much like i’ve been using SID register dumps to play songs in another project.

This is version 0.1 .. do not use.
If its wrong, or can do better please mail me.
Oh it needs a 1k resistor from the 20K’s to ground I think.

"If something is worth doing, it's worth overdoing."