반응형

아이들이 컴퓨터로 영상을 보거나 게임할 때 습관적으로 소리를 너무 크게 해 놓는 경우가 많다. 소리를 줄이라고 말해도 그때 뿐, 다시 크게 해 놓곤 한다. 시스템 볼륨에 제한을 두고 일정 수준 이상으로 올라가면 자동으로 내리는 프로그램(Turn It Down)을 만들어 보자.

 

※ 참고

2021.11.21 - [C#] - C# Media Player Mp3

2022.01.06 - [C#] - C# AudioSwitcher System Audio/Sound Volume Control - 시스템 오디오/사운드 볼륨 컨트롤 1

2023.10.28 - [C#] - C# Sound Meter 사운드 미터

 

위 링크의 내용들을 참고해 코드를 작성한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
using System;
using System.Runtime.InteropServices;
using System.Windows.Media;
 
using AudioSwitcher.AudioApi.CoreAudio;
using NAudio.CoreAudioApi;
 
namespace ConsoleApp1
{
    class Program
    {
        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();
 
        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
 
        // Usage: filename [maxVol(0~100)] [playTime(sec)]
 
        static void Main(string[] args)
        {
            const int SW_HIDE = 0// 창 숨기기
            IntPtr handle = GetConsoleWindow();
            ShowWindow(handle, SW_HIDE);
            
            MediaPlayer MP = new MediaPlayer();
            MP.Open(new Uri("turndown.mp3", UriKind.Relative));
 
            int maxVol;
            if (args.Length > 0)
                maxVol = int.Parse(args[0]);
            else
                maxVol = 20;
 
            int playTime;
            if (MP.NaturalDuration.HasTimeSpan)
                playTime = MP.NaturalDuration.TimeSpan.Milliseconds;
            else if (args.Length > 1)
                playTime = int.Parse(args[1]) * 1000;
            else
                playTime = 6000;
                // 미디어 파일 시간 정보가 없다면 적당한 값을 넣는다.
 
            MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
            MMDevice device = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Console);
            // MMDeviceEnumerator를 CoreAudioController 보다 먼저 초기화해야 한다.
 
            CoreAudioDevice defaultPlaybackDevice = new CoreAudioController().DefaultPlaybackDevice;
            
            // defaultPlaybackDevice.Volume: 시스템 볼륨(0~100)
            // device.AudioMeterInformation.MasterPeakValue: 시스템 오디오 값(0~1)
            while (true)
            {
                float masterPeakValue = device.AudioMeterInformation.MasterPeakValue;
 
                if (defaultPlaybackDevice.Volume > maxVol && masterPeakValue > 0.3f)
                    // 볼륨이 maxVol 보다 크고 오디오 값이 0.3보다 크다면..
                {                    
                    MP.Play();
                    System.Threading.Thread.Sleep(playTime); // 메세지 재생 대기
                    MP.Stop();
 
                    defaultPlaybackDevice.Volume = maxVol; // 볼륨 감소
                }
 
                System.Threading.Thread.Sleep(1000); // 1초 마다 확인
            }
        }
    }
}
 

 

 

TurnItDown.zip
1.04MB

위 압축 파일에는 실행에 필요한 모든 파일과 설치 배치파일이 들어 있다.

 

압축을 풀고 install.bat를 실행한다.

 

C드라이브 turnitdown 폴더에 프로그램이 복사된다.

 

Startup 폴더에 run.bat 파일이 복사된다.

 

 

아이들 컴퓨터에 이렇게 세팅해 놓고 재부팅하면 프로그램이 자동으로 시작된다. 물론 어떤 UI도 없기 때문에 아이들은 이 프로그램이 실행되었다는걸 인식할 수 없다. 초기 조건은 시스템 볼륨 20 이상이고 오디오로 출력되는 소리 값이 0.3 이상이면 지정한 MP3 파일이 플레이되고 볼륨이 20으로 줄어든다.

 

ex) 시스템 볼륨을 70으로 해 놓고 유튜브 영상을 보면 볼륨을 줄인다는 MP3 메세지가 플레이되고 볼륨이 20으로 줄어든다.

 

플레이되는 MP3 메세지는 C:\turnitdown 폴더의 turndown.mp3인데, 원하는 메세지를 녹음해서 바꿀 수 있다.

설치시 제공되는 MP3파일에는 내 조카(준석이)를 위한 메세지가 녹음되어 있다.

 

메세지를 바꾸거나 볼륨 제한 조건을 바꾸고 싶다면 run.bat 파일을 수정한다.

예를들어 볼륨 제한을 35로 바꾸고, 메세지가 녹음된 MP3파일이 8초로 바뀌었다면 run.bat 파일에서 위 그림과 같이 20 6으로 된 부분을 35 8로 바꾼다.

 

반응형
Posted by J-sean
:
반응형

일정한 시간(1초) 간격으로 특정 작업을 수행해 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import pygame
 
pygame.init()
pygame.display.set_caption("Super fun game development")
screen = pygame.display.set_mode((640480))
clock = pygame.time.Clock()
running = True
 
elapsedTime = 0
limitTime = 1000
count = 0
 
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
            running = False
 
    screen.fill("black")
 
    elapsedTime += clock.get_time()
    # 두 번의 Clock.tick() 호출 사이의(1 프레임) 시간을 반환한다.
    if elapsedTime < limitTime:
        pass
    else:        
        count += 1
        print("Count: " + str(count))
        print("Tick: " + str(pygame.time.get_ticks()))
        # pygame.init()이 호출된 이후 경과된 시간을 milliseconds 단위로 반환한다.
        print("FPS: " + str(clock.get_fps()))
        # Framerate을 반환한다.
        print("")
        elapsedTime = 0
    
    pygame.display.flip()
    clock.tick(60)
 
pygame.quit()
 

 

 

FPS는 60으로 유지되고 1초마다 카운트가 증가한다.

 

반응형
Posted by J-sean
:
반응형

AudioSwitcher를 이용해 시스템 볼륨을 조정해 보자.

 

AudioSwitcher Nuget Package를 설치한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
using System.Runtime.InteropServices;
using AudioSwitcher.AudioApi.CoreAudio;
 
namespace ConsoleApp1
{
    class Program
    {
        [DllImport("kernel32.dll")]
        static extern IntPtr GetConsoleWindow();
 
        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
 
        static void Main(string[] args)
        {
            const int SW_HIDE = 0// 창 숨기기
            const int SW_SHOW = 1// 창 보이기
 
            IntPtr handle = GetConsoleWindow();
            //ShowWindow(handle, SW_HIDE);
 
            CoreAudioDevice defaultPlaybackDevice = new CoreAudioController().DefaultPlaybackDevice;
            Console.WriteLine("Current Volume: " + defaultPlaybackDevice.Volume);
 
            while (true)
            {
                if (defaultPlaybackDevice.Volume > 20// 볼륨이 20 보다 크다면
                {
                    while (defaultPlaybackDevice.Volume > 20// 볼륨이 20 보다 크지 않을때 까지 무한 루프
                    {
                        defaultPlaybackDevice.Volume--// 볼륨 1 감소
                        Console.WriteLine("Current Volume: " + defaultPlaybackDevice.Volume);
                        System.Threading.Thread.Sleep(1000); // 매 1초 확인
                    }
                }
 
                System.Threading.Thread.Sleep(10000); // 매 10초 확인
            }
        }
    }
}
 

 

소스를 입력하고 빌드한다.

 

프로그램을 실행하면 시스템 볼륨이 20 이하일 때까지 1초마다 1씩 감소한다.

 

처음 35였던 볼륨이 20이 되었다.

 

2022.01.07 - [C#] - C# AudioSwitcher System Audio/Sound Volume Control - 시스템 오디오/사운드 볼륨 컨트롤 2

 

반응형
Posted by J-sean
:
반응형

유저 컨트롤(dll)을 만들고 사용해 보자.

 

Windows Forms Control Library 프로젝트를 만든다.

 

폼에 버튼과 레이블을 적당히 배치한다.

 

간단한 코드를 작성한다.

 

빌드하고 실행하면 컨트롤을 테스트하고 속성을 확인 할 수 있는 화면이 나타난다.

 

 

Output 폴더에 dll 파일이 생성 되었다.

 

이번엔 Windows Forms App 프로젝트를 만든다.

 

툴 박스에서 마우스 우클릭 - Choose Items... 를 선택한다.

 

Browse... 를 클릭한다.

 

 

위에서 만든 컨트롤 dll 파일을 선택한다.

 

유저 컨트롤이 추가 되었다. OK를 클릭한다.

 

툴 박스에 자동으로 등록된다.

 

마우스 우클릭 - Rename Item 으로 이름을 바꿀 수 있다.

 

 

폼에 적당히 배치한다.

 

추가 코드 작성 없이 빌드하고 실행하면 잘 작동한다.

실행 파일(exe)과 컨트롤 라이브러리 파일(dll)을 함께 배포해야 한다.

 

반응형
Posted by J-sean
:
반응형

MX1508 모터 드라이버로 2개의 DC모터나 1개의 스텝 모터를 컨트롤 할 수 있다.

- Supply voltage: 2~10V.

- Signal input voltage 1.8~7V.


MX1508 모터 드라이버


MX1508 드라이버 다이어그램은 아니지만 위와 같은 방식으로 연결한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
const int IN1 = 5;
const int IN2 = 6;
const int IN3 = 9;
const int IN4 = 10;
 
void setup() {
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
}
 
void loop() {
  CWRotation(100);
  delay(1000);
 
  CCWRotation(200);
  delay(1000);
}
 
void CWRotation(int speed)
{
  analogWrite(IN1, speed);
  digitalWrite(IN2, LOW);
  analogWrite(IN3, speed);
  digitalWrite(IN4, LOW);
}
 
void CCWRotation(int speed)
{
  digitalWrite(IN1, LOW);
  analogWrite(IN2,speed );
  digitalWrite(IN3, LOW);
  analogWrite(IN4, speed);
}


소스를 컴파일 하고 아두이노에 업로드한다. 모터가 다른 속도로 정회전, 역회전을 반복한다.


반응형
Posted by J-sean
:
반응형

NodeMCU(ESP-12E/ESP8266)를 이용해 LED를 제어하는 서버를 만들어 보자.


2020/03/10 - [Raspberry Pi & Arduino] - How to program ESP32-CAM using Arduino UNO - 아두이노로 ESP32-CAM 프로그래밍 하기


NodeMCU를 준비한다.


ESP8266모듈이 내장된 NodeMCU를 사용하기 위해 'Preferences - Additional Boards ManagerURLs:'에 http://arduino.esp8266.com/stable/package_esp8266com_index.json을 입력한다.


Boards Manager에서 esp를 검색하고 esp8266을 설치한다.


Tools - Board에서 NodeMCU 1.0 (ESP-12E Module)을 선택한다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <ESP8266WiFi.h>
 
const char* ssid = "Your ssid";
const char* password = "Your password";
 
WiFiServer server(80);
WiFiClient client;
String request;
int status = LOW;
 
void setup() {
  Serial.begin(9600);
 
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, LOW);
 
  Serial.print("Connecting to ");
  Serial.println(ssid);
 
  WiFi.begin(ssid, password);
  // Tells the server to begin listening for incoming connections.
 
  while(WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected!!");
  server.begin();
  Serial.println("Server started.");
 
  Serial.print("Use this URL to connect to the server: ");
  Serial.print("http://");
  Serial.println(WiFi.localIP());
}
 
void loop() {    
  client = server.available();
  // Gets a client that is connected to the server and has data available for reading.
  // The connection persists when the returned client object goes out of scope;
  // you can close it by calling client.stop().
  if (!client) {
    delay(100);
    
    return;
  }
  
  Serial.println("New client connected.");    
  
  while (!client.available()) {
    // Returns the number of bytes available for reading (that is, the amount of data
    // that has been written to the client by the server it is connected to).
    delay(100);
  }
 
  request = client.readStringUntil('\r');
  Serial.print("Request from the client: ");
  Serial.println(request);
  client.flush();
 
  if (request.indexOf("LED=ON"!= -1) {
    digitalWrite(LED_BUILTIN, HIGH);
    status = HIGH;
  } else if (request.indexOf("LED=OFF"!= -1) {
    digitalWrite(LED_BUILTIN, LOW);
    status = LOW;
  }
 
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println("");
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
  client.print("LED is turned ");
  if (status)
    client.print("On");
  else
    client.print("Off");
  client.println("<br><br>");
  client.println("<a href=\"/LED=ON\" title=\"Turn the LED on\"><button>Turn on</button></a>");
  client.println("<a href=\"/LED=OFF\" title=\"Turn the LED off\"><button>Turn off</button></a>");
  client.println("</html>");  
 
  //client.stop();
  // client.stop()을 호출하면 제대로 작동하지 않는다.
  Serial.println("Client disconnected!!");
  delay(100);
}


위 소스를 컴파일하고 NodeMCU에 업로드한다.


시리얼 모니터를 확인하면 와이파이에 연결되고 URL이 표시된다.


스마트폰이나 컴퓨터로 URL에 접속하면 위와 같은 화면이 표시된다.


'Turn on'버튼을 클릭하면 서버로 URL/LED=ON 요청을 보내고 서버에서는 LED=ON 문자열을 감지해 NodeMCU의 built-in LED를 켠다. (실제 built-in LED의 동작은 반대로 된다. LED가 꺼진다.)


'Turn off'버튼을 클릭하면 서버로 URL/LED=OFF 요청을 보내고 서버에서는 LED=OFF 문자열을 감지해 NodeMCU의 built-in LED를 끈다. (실제 built-in LED의 동작은 반대로 된다. LED가 켜진다.)


반응형
Posted by J-sean
:
반응형

내가 어릴때 가지고 놀 수 있던 장난감들은 꽤나 단순한 것들이었다. 기억해 보면 축구공, 야구공, 구슬, 팽이, 딱지, 돌(?), 하늘에서 내리는 눈 등.. 대부분 내가 노는 건지 장난감이 날 가지고 노는 건지 모를 정도의 상당한 노동력을 요하는 것들이었다. 그 덕이었는지 어릴땐 체력이 상당히 좋았던 기억이다. 물론 조금 더 크고 나서는 TV에 연결해 '내가' 가지고 놀 수 있는 가정용 게임기도 가질 수 있었지만 말이다.

 

인터넷에서 주워온 사진. 나 없음.

 

그렇게 온 몸을 사용해 놀던 어린 시절, 등장만 했다 하면 나와 내 친구들의 관심을 한 눈에 사로 잡는 동네 형의 장난감이 있었다. Radio Control Car, 바로 RC카였다. 어떤 모델이었는지도 알지 못했지만 약간의 휘발유를 넣는 작은 엔진이 달린 RC카였던거 같다. 그 형이 RC카를 들고 나오면 우리는 뭐에 홀린 사람들 처럼 하던 놀이를 중단하고 따라다니며 구경했다. 나름 엔진이 달린 꽤 큰 RC카 였기 때문에 건전지로 작동하는 작은 미니카 정도만 가질 수 있던 우리로써는 감히 상상하기 힘든 속도와 힘을 자랑했다. 지금 생각해 보면 굉음을 내며 달리는 RC카를 따라 다니며 좋다고 소리 지르는 동네 꼬마들이 그 형은 상당히 귀찮았을 것 같다. 나이 차이가 좀 났던 그 형과 친하지 않았던 우리로써는 한 번 해보게 해달라 조르지는 못하고 그저 부러운 눈빛 으로 바라보며 대리만족을 할 수밖에 없었다.

 

대충 이렇게 생겼던거 같기도 하고...

 

굳이 그런 기억이 아니더라도 대부분의 남자들은 무선 조종 장난감에 대한 호기심이 있다. 아직도 길을 걷다 RC카나 드론이 보이면 적어도 어떤 모델일지 궁금해하며 아닌척 슬쩍 눈길을 주는 내게는 그 추억이(부러움이) 상당히 컸던거 같다.

 

그래서 샀다. 알리에서.

 

주문하고 2주정도 걸렸던거 같다. 생각보다 빨리 도착 했다.

 

 

박스를 열면 RC카와 조종기가 보인다.

 

1:16 스케일의 R/C Monster Truck이라고 한다. 별다른 이름도 없다.  RC카에 대해 별로 아는게 없어서 시험삼아 싸고 대충 잘 굴러 갈거 같은 모델로 골랐다. 대략 27*17*12cm 정도 사이즈에 배송비까지 $17.20이다.

 

뒤집으면 전원 스위치와 배터리 커버가 보인다. 전륜, 후륜에 모터가 하나씩, 무려 4륜 구동이다.

 

배터리와 충전기.

 

 

전진, 후진, 좌우 조종이 가능한 컨트롤러.

 

크지 않은 장애물은 쉽게 넘는다.

 

넓은 들판에서 한 컷.

 

가파른 언덕도(40° 이상) 쫄쫄쫄 잘 올라 간다. 2만원도 안되는 가격에 꽤 잘 굴러 간다. 맘에 든다.

 

 

그래서 또 샀다. 이번엔 좀 더 큰놈으로 빨간색, 녹색 2개.

알리에서 먼저 알아 보니 1개에 $44.93 였으나 국내 쇼핑몰 가격은 배송비 포함 34,400원 이었다. 당연히 이번엔 국내 쇼핑몰에서 68,800원에 2개 구매 했다. 조카가 2명이므로.

 

이번 제품은 1:12 스케일이다. (지난번은 1:16)

 

포장을 계속 뜯어 준다.

 

사이즈는 37 x 23 x 20cm 다. 지난 번 제품보다 좀 크다.

 

 

구성품은 동일.

 

마찬가지로 전륜, 후륜에 모터가 1개씩 들어 있다.

 

2명의 조카를 위한 2개의 차량.

 

차량 크기가 커진 만큼 모터도 크고 힘이 세다.

 

 

빠르지는 않지만 언덕도 잘 올라 간다.

 

밤에는 라이트가 밝게 빛난다. 야간주행도 가능하다.

 

테스트 하다 보니 한 개의 조종기 트리거에서 문제가 발견 되었다. 전진하기 위해 트리거를 당겼다 놓으면 차가 멈추기는 하는데 트리거가 원래의 위치로(가운데로) 돌아가지 않았다. 손가락으로 살짝 밀어줘야 돌아가는 문제였다.

 

뜯어 보니 트리거에 연결된 스프링이 제대로 고정되지 않아 발생하는 문제였다. 스프링을 제자리에 고정시키고 다시 조립한다. 조립은 언제나 분해의 역순.

 

 

조카들에게 입양 되기 전 단체 기념샷. 제일 처음 구매한 차량이 가운데 귀엽게 자리 잡고 있다.

큰 차 2대를 모두 조카들에게 넘기고 작은 차 1대만 남으니 뭔가 좀 아쉬움이 남았다.

 

그래서 또 샀다... 이번엔 다시 알리에서.

 

Wltoys사의 Rock Crawler D7 12402-A 모델이다.

1:12 스케일에 최고 45Km/h의 속도가 나온다. 더 이상 아이들 장난감이 아니다. (14세 이상)

 

알리에서 $66.28에 판매 하지만 $1 할인 쿠폰 적용으로 배송비 포함 $65.28에 구매 했다.

 

 

두툼한 바퀴의 귀여운 노란색 차량과 조종기가 보인다.

 

차량을 집어 들었는데 안나와서 살펴 보니 케이블 타이로 묶여 있었다. 풀어 준다.

 

차량의 바퀴를 보면 전륜은 바깥쪽을 향해 벌어진 것을 볼 수 있다. Toe-in, Toe-out 이라는걸 처음 알게 되었다. 불량인줄 알고 드라이버 들고 뜯으려 했었다...

 

이 조종기에는 기본적인 동작 컨트롤 외, 차량의 전륜 및 스로틀 조절 다이얼이 있다.

 

 

 

차량을 뒤집으면 전원 스위치와 배터리 커버가 보인다. 배터리 커버는 드라이버 없이 간단히 열고 닫을 수 있다.

 

충전기와 간단한 메뉴얼이 들어 있다.

 

 

 

반응형
Posted by J-sean
: