All posts by fash

5 sensor Flame detector

December is coming, time for candles.

The plan is to make a flame sensor, with in combination with my presence sensor will alert me when we leave the room and candles are on, give us a notification.

Above is the schematic. A Wemos mini (left over from another project), an analog multiplexer and a cheap 5 times flame detector.
There is a potentiometer on this board to change the sensitivity.

Search “Infrared Ir Flame Sensor Detector Fire Detection Module 5 Channel” on Aliexpress. These are 1 euro.

Presence sensor:

Here is some Arduino code.

#define MUX_A D5
#define MUX_B D6
#define MUX_C D7
#define ENABLE D1

#define ANALOG_INPUT A0


void setup() {
    Serial.begin(9600);

  //Define output pins for Mux
  pinMode(MUX_A, OUTPUT);
  pinMode(MUX_B, OUTPUT);     
  pinMode(MUX_C, OUTPUT);     
  pinMode(ENABLE, OUTPUT);     
  digitalWrite(ENABLE, LOW); // No need to switch, can be permanenty low
  
}

void changeMux(int c, int b, int a) {
  digitalWrite(MUX_A, a);
  digitalWrite(MUX_B, b);
  digitalWrite(MUX_C, c);
}

void loop() {
  float value;
  
  
  changeMux(LOW, LOW, HIGH);
  value = analogRead(ANALOG_INPUT); //Value of the sensor connected Option 1 pin of Mux
  Serial.print("Variable_1:");
  Serial.print(value);
  Serial.print(",");

  changeMux(LOW, HIGH, LOW);
  value = analogRead(ANALOG_INPUT); //Value of the sensor connected Option 2 pin of Mux
  Serial.print("Variable_2:");
  Serial.print(value);
  Serial.print(",");

  changeMux(LOW, HIGH, HIGH);
  value = analogRead(ANALOG_INPUT); //Value of the sensor connected Option 3 pin of Mux
  Serial.print("Variable_3:");
  Serial.print(value);
  Serial.print(",");

  changeMux(HIGH, LOW, LOW);
  value = analogRead(ANALOG_INPUT); //Value of the sensor connected Option 4 pin of Mux
  Serial.print("Variable_4:");
  Serial.print(value);
  Serial.print(",");

  changeMux(HIGH, LOW, HIGH);
  value = analogRead(ANALOG_INPUT); //Value of the sensor connected Option 5 pin of Mux
  Serial.print("Variable_5:");
  Serial.println(value);
}

ESPHOME Code

esphome:
  name: flamedetector
  friendly_name: FlameDetector
  on_boot:
    priority: 600
    then:
      - switch.turn_off: enable 

esp8266:
  board: d1_mini

# Enable logging
logger:

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

ota:
  - platform: esphome
    password: "780fe0xxxxxxxxxxxxxxxxxxxxx7859"

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

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

captive_portal:

switch:
  - platform: gpio
    id: enable
    pin:
      number: D1

cd74hc4067:
  - id: cd74hc4067_1
    pin_s0: D5
    pin_s1: D6
    pin_s2: D7
    pin_s3: D2

globals:
  - id: last_flame_state
    type: bool
    restore_value: no
    initial_value: 'false'
  - id: trigger_on
    type: bool
    restore_value: no
    initial_value: 'false'

sensor:
  - platform: adc
    id: adc_sensor
    pin: A0
    name: flameanalog
    update_interval: 10s
  - platform: cd74hc4067
    id: flame_1_1
    name: flame_1_1
    number: 1
    sensor: adc_sensor
    update_interval: 10s
  - platform: cd74hc4067
    id: flame_1_2
    name: flame_1_2
    number: 2
    sensor: adc_sensor
    update_interval: 10s
  - platform: cd74hc4067
    id: flame_1_3
    name: flame_1_3
    number: 3
    sensor: adc_sensor
    update_interval: 10s
  - platform: cd74hc4067
    id: flame_1_4
    name: flame_1_4
    number: 4
    sensor: adc_sensor
    update_interval: 10s
  - platform: cd74hc4067
    id: flame_1_5
    name: flame_1_5
    number: 5
    sensor: adc_sensor
    update_interval: 10s


interval:
  - interval: 10s
    then:
      - lambda: |-
          const float threshold = 0.01;
          bool any_above =
            id(flame_1_1).state > threshold ||
            id(flame_1_2).state > threshold ||
            id(flame_1_3).state > threshold ||
            id(flame_1_4).state > threshold ||
            id(flame_1_5).state > threshold;

          // Store the result
          if (any_above != id(last_flame_state)) {
            id(last_flame_state) = any_above;
            if (any_above) {
              ESP_LOGI("flame_check", "Flame detected (turning ON)");
              id(trigger_on) = true;
            } else {
              ESP_LOGI("flame_check", "No flame detected (turning OFF)");
              id(trigger_on) = false;
            }
          }

  - interval: 1s
    then:
      - if:
          condition:
            lambda: 'return id(trigger_on);'
          then:
            - homeassistant.service:
                service: homeassistant.turn_on
                data:
                  entity_id: input_boolean.testtoggle
          else:
            - homeassistant.service:
                service: homeassistant.turn_off
                data:
                  entity_id: input_boolean.testtoggle

C64 ROM switcher, Hidden cam viewer, Motorized fader pot and PCB sponsor

Lol, got an email from a PCB sponsor!

Deeply impressed by your blog content which means a lot to electronic enthusiasts.
I'd like to sponsor your project by providing free PCB prototyping,

ROM SWITCHER

I made a rom switcher in the past.
Now I’m using an Arduino to switch Kernal and Character rom.
(Partly idea from Adrian)

Where the F* is my schematic. Ah here it is.

Above right picture:

  • Tactile button (emulates restore key)
  • Red led – reset
  • Yellow led – Exrom
  • Blue leds, Address lines select ROM part in 27512 EPROM
  • Green leds, Address lines select ROM part Character ROM

Motorized Fader Potentiometer

I don’t trust some B&B’s so I made a camera detector.
(I always scan the wifi and Access Points)
This one lets you know if there are IR enabled camera’s.
(Night vision)

Picture 1:

  • 1 = org camera module, IR filter is hard to remove. (See pink color)
  • 2 = other module, IR filter is at bottom
  • 3 = IR Filter, I removed this.

Picture 2:

IR light blast from a “hidden” camera. (I need to adjust focus of lens)

Three display C64 audio monitor

While I made this for my Commodore C64, it is applicable for many things.

It started with some cheap displays from Ali, and some leftover Wemos D1 from my Pressure Lab project.

I Started measuring the audio output from sound devices and from my C64.
I soon discovered that I needed some way to get the offset and amplification correct for the analogue input of a Wemos. (0-3v3)

So a little op-amp circuit was born, but not without some struggles.
I forgot many things about amplifiers. It was one of the first school books I got rid of. (Sorry mister Rafaela)

After searching the internet and posting a question on Reddit I ended up with the following.

R1 and R2 are 100M. The potentiometer P1 allows me to set the offset.
R3 is 1M
C1 is 100nF to decouple the audio signal from the RCA.

R4 is 47K and C2 is 330nF (thanks tycho205)
Cimportant=1/(2πfR2)
where f is the lowest frequency of interest. In this case Cimportant should be about 330nF

LM324 is a quad amplifier, leftover from another project.
Note, the SINGLE RAIL power.

P2 potentiometer is 2M (leftover) and gives me a variable amplifying opportunity.


A = Audio input

B = Setting the offset with P1

C = Setting the amplification

Below input signal (note negative values) above amplified signal with offset!

The displays are 3 Wemos controllers with a cheap I2C display.
These are just fast enough to do FFT.

Analogue in is the output from the OP-amp offsetter ..

CODE

Needs cleaning up, and a better stabilize routine.

Signal generator and Midi progress

Yesterday I got my new signal generator (FY6900), my DIY version was missing functions.
Now I can test my op-amp schematic I’ve been posting about.

MIDI Controller

Next, for my YM2203 player, I wanted to make this Midi Controllable.
But I don’t have a Midi Controller which has enough knobs and keys.
Besides that, I thought it was cool to build my own.

  • Teensy 4.0 (because I needed multiple analogue inputs, I used another controller before, with an analogue multiplexer. But I wanted to use the easy method to use USB as midi also.
    The board will also have midi connectors, but not populated yet.
    So you can use both.
  • SSD1306 display (for now)
    Showing note being played and two potentiometer values.
  • Two potentiometers (for now) I can use these separately. But when pressing the last key on the key matrix, it will change the CC Channel of the first pot. (Using the value of the second)
  • 4×4 Matrix keyboard being read by a MCP23017 I2C 16-bit GPIO Expander. Both display and GPIO expander are on the same bus.

MCP23017 pinout

I used some pullups on : /RESET and GPA0-3
Pullups not needed on SDA/CLK because these are on the display board.

Midi control YM2203 and Analog AMP stuff

While watching TV, I drew some schematics for the YM2203 player.

For another project, I was researching the best way to get the voltage levels perfect for below analogue input.

(See the little red breadboard in the right picture above! )

Damn, I couldn’t do this stuff in school. Tried to forget it all … now I need it!

Top of this image shows what I want to accomplish. Audio or other input which has negative values also, convert to another voltage range to be read by analog read on an Wemos. These can only read 0-3v3

YM2203 – next part – Midi

I took yesterday’s setup and added polyphonic Midi.

Schematic same as previous post, just added a Midi Shield.

I can use my QY100 as midi keyboard. But playing midi files is also fun!

Because it isn’t using the right instrument and not all channels are being used.

I wanted to make a register replacer, which utilizes a sdcard reader and music dump to play songs.
But I think I’m going to make a full-blown Midi song player for the YM2203.

YM2203 Player

UPDATE: FM using the DAC is also working

At the hackercamp I mentioned a few posts ago, I met a guy who was into synths and we started talking.
We discovered we have much in common.

I showed him my AY-3-8910 player, but he had some other sound chips he wanted to play with.

After we found out which ones, I decided to buy some.
And last week I started building a player for it.

Ultimate goal is to make this midi enabled.

The chip in question is a YM2203 from Yamaha. It has three channels and can do FM sounds.

Today the first sounds! (2 channels)

  • Arduino UNO (clone)
  • YM2203
  • YM3014 DAC
  • LM324 Amplifier
  • 1Mhz crystal (that’s what I could find)

Below is the schematic, code will follow. (Needs a lot of cleanup)

I used A1 for reset signal, and have not used the complete amplifying part

I think I’m going to make some kind of library/class.
For easy programming.

FM via DAC

CODE FOR PSG (Programmable Sound Generator)
(FM code will follow)

// --- Pin definitions for Arduino Uno ---
#define YM_D0   2   // D0–D7 on Arduino D2–D9
#define YM_A0   10  // Address select
#define YM_WR   11  // Write
#define YM_RD   12  // Read
#define YM_CS   13  // Chip Select
#define YM_RST  A1  // Reset

// CLK is 1Mhz
#define CLK_FREQ  1000000UL

struct Note {
  uint16_t freq; // Hz
  uint16_t dur;  // ms
  uint16_t vol;  // ms
};

// --- Simple melody table (frequencies in Hz) ---
Note melody[] = {
  {440, 400, 15}, {494, 400, 15}, {523, 400, 15}, {587, 400, 15}, // A4, B4, C5, D5
  {659, 400, 15}, {698, 400, 15}, {784, 800, 15},             // E5, F5, G5
  {0, 400, 0} // rest
};

const int melodyLength = sizeof(melody) / sizeof(Note);

// --- Setup I/O pins for YM2203 ---
void setupPins() {
  pinMode(YM_CS, OUTPUT);
  pinMode(YM_WR, OUTPUT);
  pinMode(YM_RD, OUTPUT);
  pinMode(YM_A0, OUTPUT);
  pinMode(YM_RST, OUTPUT);

  for (int i = 0; i < 8; i++) {
    pinMode(YM_D0 + i, OUTPUT);
  }

  digitalWrite(YM_CS, HIGH);
  digitalWrite(YM_WR, HIGH);
  digitalWrite(YM_RD, HIGH);
  digitalWrite(YM_RST, HIGH);
}

// --- Put 8-bit value on D0–D7 bus ---
void setDataBus(uint8_t val) {
  for (int i = 0; i < 8; i++) {
    digitalWrite(YM_D0 + i, (val >> i) & 1);
  }
}

// --- Write register/data to YM2203 ---
void ymWrite(uint8_t reg, uint8_t val) {
  digitalWrite(YM_CS, LOW);

  // Send register address
  digitalWrite(YM_A0, LOW);
  setDataBus(reg);
  digitalWrite(YM_WR, LOW); delayMicroseconds(1);
  digitalWrite(YM_WR, HIGH);

  // Send data
  digitalWrite(YM_A0, HIGH);
  setDataBus(val);
  digitalWrite(YM_WR, LOW); delayMicroseconds(1);
  digitalWrite(YM_WR, HIGH);

  digitalWrite(YM_CS, HIGH);
}

// --- Set PSG Channel A frequency ---
void setChannelAFreq(uint16_t freq) {
  if (freq == 0) {  // rest
    ymWrite(0x08, 0x00); // volume 0 (mute)
    return;
  }

  // YM2203 PSG frequency formula: Fout = clock / (16 * N)
  // N = 12-bit value
  uint16_t N = CLK_FREQ / (16UL * freq);

  ymWrite(0x00, N & 0xFF);         // Freq LSB
  ymWrite(0x01, (N >> 8) & 0x0F);  // Freq MSB
}

// --- Set PSG Channel B frequency ---
void setChannelBFreq(uint16_t freq) {
  if (freq == 0) {  // rest
    ymWrite(0x09, 0x00); // volume 0 (mute)
    return;
  }

  // YM2203 PSG frequency formula: Fout = clock / (16 * N)
  // N = 12-bit value
  uint16_t N = CLK_FREQ / (16UL * freq);

  ymWrite(0x02, N & 0xFF);         // Freq LSB
  ymWrite(0x03, (N >> 8) & 0x0F);  // Freq MSB
}

// --- Set PSG Channel C frequency ---
void setChannelAFreq(uint16_t freq) {
  if (freq == 0) {  // rest
    ymWrite(0x0A, 0x00); // volume 0 (mute)
    return;
  }

  // YM2203 PSG frequency formula: Fout = clock / (16 * N)
  // N = 12-bit value
  uint16_t N = CLK_FREQ / (16UL * freq);

  ymWrite(0x04, N & 0xFF);         // Freq LSB
  ymWrite(0x05, (N >> 8) & 0x0F);  // Freq MSB
}

// --- Set PSG Channel A volume ---
void setChannelAVol(uint8_t vol) {
  ymWrite(0x08, vol);             
}
// --- Set PSG Channel B volume ---
void setChannelAVol(uint8_t vol) {
  ymWrite(0x09, vol);             
}
// --- Set PSG Channel C volume ---
void setChannelAVol(uint8_t vol) {
  ymWrite(0x0A, vol);             
}

// --- Arduino setup ---
void setup() {
  setupPins();

  // Reset YM2203
  digitalWrite(YM_RST, LOW);
  delay(100);
  digitalWrite(YM_RST, HIGH);
  delay(100);

  // Enable PSG Channel A tone, disable noise
  ymWrite(0x07, 0b11111110); // bit0=0 enables tone A
//  ymWrite(0x07, 0b11111101); // bit1=0 enables tone B
//  ymWrite(0x07, 0b11111011); // bit2=0 enables tone C
}

// --- Play melody in loop ---
void loop() {
  for (int i = 0; i < melodyLength; i++) {
    setChannelAFreq(melody[i].freq);
    setChannelAVol(melody[i].vol);
    delay(melody[i].dur);
  }
}

Retro stuff

A new “old” book, printed via Lulu

Plexiglass case for my collection of motherboards (8088,8086,386 and 486)
See this post:

A Ben Eater 6502 add-on, A bus monitor using ULN2803A.

Not all green/yellow wires are drawn. You can figure out where to add them.

I made these on a breadboard long ago, these will be made on permboards. (Like my Clock module on steroids.

ULN2803 will draw nearly no power from your bus, but they invert the signal, that’s why I rotated the ledbars and attached them to VCC.

UPDATE: Added other modules

Little Oled audio monitors.

UPDATE : Better waveform display

I wanted to make a SID monitor to display the 3 Channels of a C64 SID chip. This requires 3 SID chips, so this is a work-in-progress.

I bought several display I2C 128*32 in China, for cheap .. to have some stock .. So much for having stock.

But monitoring works! I’ve made 3 setups using a Wemos D1

Monitoring, scrolling monitoring and FFT analysis.

When mode selector using a button works, I’ll post the code.

UPDATE : New waveform display, better centered.

Meanwhile, practice SMD soldering.

Mini midi monitor

Two versions of a mini monitor

A version using a Arduino and a Midi shield (Yellow wires are for display)
D0 (RX) is used for the Midi IN signal.

10K pullups SDA/CLK

Above a Teensy 4.0 version. This one uses MIDI over USB.

Next to add: Rotary encoders, to select a CC Channel and display values graphically

CODE for Teensy version

#include <U8g2lib.h>
#include <Wire.h>
#include <MIDIUSB.h>   // Teensy's built-in USB MIDI

// SH1106 128x64 I2C constructor
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

void setup() {
  u8g2.begin();
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_6x12_tf);
  u8g2.drawStr(0,12,"MIDI Monitor Ready");
  u8g2.sendBuffer();
}

void loop() {
  // Check for incoming MIDI

  while (usbMIDI.read()) {
  u8g2.clearBuffer();
  u8g2.setFont(u8g2_font_6x12_tf);
  u8g2.drawStr(0,12,"MIDI Monitor Ready");
  u8g2.sendBuffer();
    byte type = usbMIDI.getType();
    byte channel = usbMIDI.getChannel();
    byte data1 = usbMIDI.getData1();
    byte data2 = usbMIDI.getData2();

    int y = 24;

    if (type == usbMIDI.NoteOn && data2 > 0) {
      u8g2.setCursor(0, y);      u8g2.print("Note ON     "); // pad
      u8g2.setCursor(0, y+12);   u8g2.printf("Ch:%-3d", channel);  // pad width 3
      u8g2.setCursor(0, y+24);   u8g2.printf("Note:%-3d", data1);
      u8g2.setCursor(0, y+36);   u8g2.printf("Vel:%-3d", data2);
    } 
    else if (type == usbMIDI.NoteOff || (type == usbMIDI.NoteOn && data2 == 0)) {
      u8g2.setCursor(0, y);      u8g2.print("Note OFF    "); // pad
      u8g2.setCursor(0, y+12);   u8g2.printf("Ch:%-3d", channel);
      u8g2.setCursor(0, y+24);   u8g2.printf("Note:%-3d", data1);
    } 
    else if (type == usbMIDI.ControlChange) {
      u8g2.setCursor(0, y);      u8g2.print("Control Chg ");
      u8g2.setCursor(0, y+12);   u8g2.printf("Ch:%-3d", channel);
      u8g2.setCursor(0, y+24);   u8g2.printf("CC#:%-3d", data1);
      u8g2.setCursor(0, y+36);   u8g2.printf("Val:%-3d", data2);
    } 
    else {
      u8g2.setCursor(0, y);      u8g2.print("Other MIDI  ");
      u8g2.setCursor(0, y+12);   u8g2.printf("Type:%-3d", type);
    }

    u8g2.sendBuffer();
  }
}

CODE for Arduino plus shield

#include <U8g2lib.h>
#include <Wire.h>
#include <MIDI.h>   // FortySevenEffects MIDI library

// SH1106 128x64 I2C (page buffer, low RAM)
U8G2_SH1106_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);

// MIDI on hardware Serial (RX=D0)
MIDI_CREATE_INSTANCE(HardwareSerial, Serial, MIDI);

char line[12];  // small buffer for formatting

void setup() {
  u8g2.begin();
  u8g2.setFont(u8g2_font_6x12_tf); // proportional font, small size

  // Initial message
  u8g2.firstPage();
  do {
    u8g2.setCursor(0, 12);
    u8g2.print("MIDI Monitor Ready");
  } while (u8g2.nextPage());

  MIDI.begin(MIDI_CHANNEL_OMNI);  // listen to all channels
}

void loop() {
  if (MIDI.read()) {
    byte type    = MIDI.getType();
    byte channel = MIDI.getChannel();
    byte data1   = MIDI.getData1();
    byte data2   = MIDI.getData2();

    // Page buffer redraw
    u8g2.firstPage();
    do {
      // Title
      u8g2.setCursor(0, 12);
      u8g2.print("MIDI Monitor");

      int y = 24; // start lower down

      if (type == midi::NoteOn && data2 > 0) {
        u8g2.setCursor(0, y);      
        u8g2.print("Note ON   ");

        snprintf(line, sizeof(line), "Ch:%-3d", channel);
        u8g2.setCursor(0, y+12);   u8g2.print(line);32

        snprintf(line, sizeof(line), "Note:%-3d", data1);
        u8g2.setCursor(0, y+24);   u8g2.print(line);

        snprintf(line, sizeof(line), "Vel:%-3d", data2);
        u8g2.setCursor(0, y+36);   u8g2.print(line);
      } 
      else if (type == midi::NoteOff || (type == midi::NoteOn && data2 == 0)) {
        u8g2.setCursor(0, y);      
        u8g2.print("Note OFF  ");

        snprintf(line, sizeof(line), "Ch:%-3d", channel);
        u8g2.setCursor(0, y+12);   u8g2.print(line);

        snprintf(line, sizeof(line), "Note:%-3d", data1);
        u8g2.setCursor(0, y+24);   u8g2.print(line);
      } 
      else if (type == midi::ControlChange) {
        u8g2.setCursor(0, y);      
        u8g2.print("Control Chg");

        snprintf(line, sizeof(line), "Ch:%-3d", channel);
        u8g2.setCursor(0, y+12);   u8g2.print(line);

        snprintf(line, sizeof(line), "CC#:%-3d", data1);
        u8g2.setCursor(0, y+24);   u8g2.print(line);

        snprintf(line, sizeof(line), "Val:%-3d", data2);
        u8g2.setCursor(0, y+36);   u8g2.print(line);
      } 
      else {
        u8g2.setCursor(0, y);      
        u8g2.print("Other MIDI");

        snprintf(line, sizeof(line), "Type:%-3d", type);
        u8g2.setCursor(0, y+12);   u8g2.print(line);
      }
    } while (u8g2.nextPage());
  }
}