Electrónica y programación para Microcontroladores.

Libros técnicos para electrónica programable.

Email
Contactanos en:

consultas@firtec.com.ar

Arduino

Originalmente mi casa tiene un timbre que se instaló hace muchos años, tiene un sonido muy agradable con el clásico “ding-dong” pero por su edad es electro-mecánico con un solenoide que empuja una pequeña barra metálica que a su vez golpea una campana que produce el sonido.
Realmente funciona muy bien y me gusta el sonido pero para su funcionamiento tiene que estar cableado con el interruptor que se encuentra en la calle.
Sumado a que su sonido no llega a todos los puntos de la casa y mucho menos a la planta alta y aprovechando el descanso de verano me propuse mejorar el alcance de este timbre pero sin cambiarlo de lugar ni cambiar el circuito eléctrico, una solución sería subir el estado del timbre a un enlace inalámbrico que será leído desde cualquier parte de la casa.  
Como no puedo reproducir el exacto sonido que tiene el timbre original por ser mecánico se me ocurrió construir un receptor que cuando reciba información de que hay alguien en la puerta “hable” mediante un sintetizador de voz diciendo la novedad que hay gente tocando el timbre.
En realidad todo el proyecto ha sido construido como hobby reciclando componentes de proyectos anteriores, el emisor se colocó en una pequeña caja plástica junto al timbre original y todo el receptor se colocó en una caja que contiene parlante y amplificador de un viejo sistema multimedia en desuso.
Para el enlace de radio un par de APC220 también sobrantes de otro proyecto pero cualquier enlace de radio funcionará ya que solo debe comunicarle al receptor la activación del timbre.
Para la detección del voltaje en el solenoide del timbre usé un PC817 conectado a un transistor BC337. Este transistor coloca un nivel bajo el pin IRQ 2 de un Arduino Nano disparando una interrupción.
Cuando la interrupción se dispara se envía un un byte comando por el enlace de radio, cuando el receptor recibe la transmisión decodifica el mensaje y si está el byte comando envía al sintetizador de voz la frase o mensaje que debe reproducir.
En lugar del sintetizador de voz se podría conectar un piezoeléctrico o cualquier sistema para avisar que el timbre de calle se activó, yo use un sintetizador porque ya lo tenía.
En el emisor un Arduino Nano y en el receptor un Arduino Mega Pro ya que necesitaba algo pequeño para colocar en la caja del parlante y el Nano no puede manejar el sintetizador de voz.

Circuitos de emisor y receptor.

El código del emisor contenido en el Arduino Nano es bastante simple, solo debe enviar un byte comando cada vez que ocurra una interrupción en el Pin 2.

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
/********************************************************************
* Recoge el estado de activación de la campanilla y envía la novedad
* por un enlace de radio.
*********************************************************************/
#include <avr/wdt.h>
#include <avr/sleep.h>
#define ledPin 13
#define power_tx 4
byte Timbre_SI= 0x55; // Código para indicar que el timbre se activó
 
void setup (){
   pinMode(2,INPUT_PULLUP); 
   Serial.begin(9600); 
   pinMode(13, OUTPUT);
   digitalWrite(13,LOW );
   pinMode(power_tx, OUTPUT);
   digitalWrite(power_tx,LOW );
   attachInterrupt( 0, ISR_Timbre, LOW);
   wdt_disable();
   wdt_enable(WDTO_4S);  
 }  
 
void ISR_Timbre(){     
              wdt_reset(); 
              delay(2500);
              digitalWrite(power_tx,HIGH );
              Serial.write(Timbre_SI); 
              digitalWrite(13,HIGH );
              }
 
void loop (){
   digitalWrite(13,LOW );
   digitalWrite(power_tx,LOW );
    wdt_reset(); 
}

 

Para asegurar el correcto funcionamiento tanto el emisor como el receptor tienen el Watch Dog del microcontrolador activado con un tiempo fijo de 4 segundos mas que suficiente para mantener el sistema controlado.
El código del Arduino Mega es el siguiente.

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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
#include <avr/pgmspace.h>
#include <avr/wdt.h>
#include <SPI.h>
#include <Wire.h>
#include <string.h>
#include <math.h>
#include "text_to_speech_img.h"
#define S1V30120_RST    49
#define S1V30120_RDY    10
#define S1V30120_CS     53
#define S1V30120_MUTE   A0
//------------------------------------------
 
#define F_CPU 16000000
#define USART_BAUDRATE 9600
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
#define led 13
#define sirena 4
 
volatile byte dato =0;
volatile byte bandera = 0;
 
// Define parameteros para S1V30120
 
#define ISC_VERSION_REQ      0x0005
#define ISC_BOOT_LOAD_REQ   0x1000
#define ISC_BOOT_RUN_REQ    0x1002
#define ISC_TEST_REQ      0x0003
#define ISC_AUDIO_CONFIG_REQ  0x0008
#define ISC_AUDIO_VOLUME_REQ  0x000A
#define ISC_AUDIO_MUTE_REQ    0x000C
#define ISC_TTS_CONFIG_REQ    0x0012
#define ISC_TTS_SAMPLE_RATE   0x01
#define ISC_TTS_VOICE     0x00
#define ISC_TTS_EPSON_PARSE   0x01
#define ISC_TTS_LANGUAGE      0x04   /// Lenguaje español
#define ISC_TTS_SPEAK_RATE_LSB  0xC8
#define ISC_TTS_SPEAK_RATE_MSB  0x00
#define ISC_TTS_DATASOURCE    0x00
#define ISC_TTS_SPEAK_REQ     0x0014
#define ISC_VERSION_RESP    0x0006
#define ISC_BOOT_LOAD_RESP    0x1001
#define ISC_BOOT_RUN_RESP     0x1003
#define ISC_TEST_RESP       0x0004
#define ISC_AUDIO_CONFIG_RESP   0x0009
#define ISC_AUDIO_VOLUME_RESP 0x000B
#define ISC_AUDIO_MUTE_RESP   0x000D
#define ISC_TTS_CONFIG_RESP   0x0013
#define ISC_TTS_SPEAK_RESP    0x0015
#define ISC_ERROR_IND     0x0000
#define ISC_MSG_BLOCKED_RESP  0x0007
#define ISC_TTS_FINISHED_IND  0x0021
#define TTS_AUDIO_CONF_AS   0x00
#define TTS_AUDIO_CONF_AG   0x43
#define TTS_AUDIO_CONF_AMP  0x00
#define TTS_AUDIO_CONF_ASR  0x01
#define TTS_AUDIO_CONF_AR   0x00
#define TTS_AUDIO_CONF_ATC  0x00
#define TTS_AUDIO_CONF_ACS  0x00
#define TTS_AUDIO_CONF_DC   0x00
//-----------------------------------------------------
 
void S1V30120_reset(void);
unsigned short S1V30120_get_version(void);
bool S1V30120_download(void);
bool S1V30120_load_chunk(unsigned short);
bool S1V30120_boot_run(void);
void show_response(bool);
bool S1V30120_registration(void);
bool S1V30120_parse_response(unsigned short, unsigned short, unsigned short);
void S1V30120_send_padding(unsigned short);
void S1V30120_send_message(volatile char, unsigned char);
bool S1V30120_configure_audio(void);
bool S1V30120_configure_tts(void);
bool S1V30120_speech(String , unsigned char);
bool S1V30120_set_volume(void);
bool S1V30120_load_chunk(unsigned short);
String m0 = "Atencion atencion";
String m1 = "Estan tocando el timbre";
String m2 = "Sistema listo y atento";
char rcvd_msg[20] = {0};
static volatile char send_msg[200] = {0};
static volatile unsigned short msg_len;
static volatile unsigned short txt_len;
 
unsigned short tmp;
long idx;
bool success;
 
static volatile unsigned short TTS_DATA_IDX;
 
void setup() {
   pinMode(led, OUTPUT);
   digitalWrite(led,LOW );
  pinMode(S1V30120_RST, OUTPUT);
  pinMode(S1V30120_RDY, INPUT);
  pinMode(S1V30120_CS, OUTPUT);
  pinMode(S1V30120_MUTE, OUTPUT);
 
  // Unmute
  digitalWrite(S1V30120_MUTE,LOW);
 
 // Configura los baudios
  UBRR2H = (uint8_t)(UBRR_VALUE >> 8);
  UBRR2L = (uint8_t)UBRR_VALUE;
  UCSR2C |= (1 << UCSZ20) | (1 << UCSZ21); 
  UCSR2B |= (1 << RXEN2) | (0 << TXEN2) | (1 << RXCIE2);        
  UCSR2A = 0x00; 
  interrupts(); 
  bandera = 0; 
  SPI.begin();   
  S1V30120_reset();
  success = S1V30120_download();
  success = S1V30120_boot_run();
  show_response(success);
  delay(150); 
  success = S1V30120_registration();
  show_response(success);
  S1V30120_get_version();
  success = S1V30120_configure_audio();
  show_response(success);
  success = S1V30120_set_volume();
  show_response(success);
  success = S1V30120_configure_tts();
  show_response(success);
  show_response(success);
  success = S1V30120_speech(m2,1);
  wdt_enable(WDTO_4S); 
}
/***************************************************************************
 * Función ISR para la UART2.
 * Procesa el caracter recibido. Si es 0x55 cambia bandera
 * Argumentos: NINGUNO
 * Retorna: Bandera
 *
****************************************************************************/
ISR(USART2_RX_vect){   
  byte RX_Byte = UDR2;; 
  UDR2 = RX_Byte;
  dato = UDR2;
   if(dato == 0x55)
      bandera = 1;
   dato = 0;
 }
 
//####################################################################
void loop() {
  if(bandera == 1){
              wdt_reset();
              success = S1V30120_speech(m0,1);
              delay(500);
              wdt_reset();
              success = S1V30120_speech(m1,1);  
              bandera = 0;
        }
    wdt_reset();
}
//########################################################################
 
void S1V30120_reset(void)
{
  digitalWrite(S1V30120_CS,HIGH); 
  digitalWrite(S1V30120_RST,LOW);
  SPI.beginTransaction(SPISettings(750000, MSBFIRST, SPI_MODE3));
  SPI.transfer(0x00);
  SPI.endTransaction();
  delay(200);
  digitalWrite(S1V30120_RST,HIGH);
  delay(200);
}
 
unsigned short S1V30120_get_version(void)
{
    unsigned short S1V30120_version = 0;
    unsigned short tmp_disp;
    char msg_ver[] = {0x04, 0x00, 0x05, 0x00};
    S1V30120_send_message(msg_ver, 0x04);
    while(digitalRead(S1V30120_RDY) == 0);
    digitalWrite(S1V30120_CS,LOW);
    SPI.beginTransaction(SPISettings(750000, MSBFIRST, SPI_MODE3));
    while(SPI.transfer(0x00) != 0xAA);
    for (int i = 0; i < 20; i++)
    {
      rcvd_msg[i]= SPI.transfer(0x00);
    }
    S1V30120_send_padding(16);
    SPI.endTransaction();
    digitalWrite(S1V30120_CS,HIGH);
    S1V30120_version = rcvd_msg[4] << 8 | rcvd_msg[5];
    Serial.print("HW version ");
    Serial.print(rcvd_msg[4],HEX);
    Serial.print(".");
    Serial.println(rcvd_msg[5],HEX);
    Serial.print("Firmware version ");
    Serial.print(rcvd_msg[6],HEX);
    Serial.print(".");
    Serial.print(rcvd_msg[7],HEX);
    Serial.print(".");
    Serial.println(rcvd_msg[16],HEX);
    Serial.print("Firmware features ");
    Serial.println(((rcvd_msg[11] << 24) | (rcvd_msg[10] << 16) | (rcvd_msg[9] << 8) | rcvd_msg[8]),HEX);
    Serial.print("Firmware extended features ");
    Serial.println(((rcvd_msg[15] << 24) | (rcvd_msg[14] << 16) | (rcvd_msg[13] << 8) | rcvd_msg[12]),HEX);
    return S1V30120_version;
}
 
bool S1V30120_load_chunk(unsigned short chunk_len)
{
  char len_msb = ((chunk_len + 4) & 0xFF00) >> 8;
  char len_lsb = (chunk_len + 4) & 0xFF;
  digitalWrite(S1V30120_CS,LOW);
  SPI.beginTransaction(SPISettings(750000, MSBFIRST, SPI_MODE3));
  SPI.transfer(0xAA);  
  SPI.transfer(len_lsb);  
  SPI.transfer(len_msb);  
  SPI.transfer(0x00); 
  SPI.transfer(0x10);
  for (int chunk_idx = 0; chunk_idx < chunk_len; chunk_idx++)
  {
    SPI.transfer(pgm_read_byte_near(TTS_INIT_DATA + TTS_DATA_IDX));
    TTS_DATA_IDX++;
  }
  SPI.endTransaction();
  digitalWrite(S1V30120_CS,HIGH);
  return S1V30120_parse_response(ISC_BOOT_LOAD_RESP, 0x0001, 16);
}
 
bool S1V30120_download(void)
{
 
   unsigned short len = sizeof (TTS_INIT_DATA);
   unsigned short fullchunks;
   unsigned short remaining;
   bool chunk_result;
   long data_index = 0;
   fullchunks = len / 2044;
   remaining = len - fullchunks * 2044;
return 1;
}
 
bool S1V30120_boot_run(void)
{
    char boot_run_msg[] = {0x04, 0x00, 0x02, 0x10};
    S1V30120_send_message(boot_run_msg, 0x04);
    return S1V30120_parse_response(ISC_BOOT_RUN_RESP, 0x0001, 8);
}
 
void show_response(bool response)
{
  if(response)
    Serial.println("OK!");
  else
  {
    Serial.println("Failed. System halted!");
    while(1);
  }
}
 
bool S1V30120_registration(void)
{
  SPI.beginTransaction(SPISettings(750000, MSBFIRST, SPI_MODE3));
  char reg_code[] = {0x0C, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  S1V30120_send_message(reg_code, 0x0C);
  return S1V30120_parse_response(ISC_TEST_RESP, 0x0000, 16);
}
 
bool S1V30120_parse_response(unsigned short expected_message, unsigned short expected_result, unsigned short padding_bytes)
{
    unsigned short rcvd_tmp;
    while(digitalRead(S1V30120_RDY) == 0);
    digitalWrite(S1V30120_CS,LOW);
    SPI.beginTransaction(SPISettings(750000, MSBFIRST, SPI_MODE3));
    while(SPI.transfer(0x00) != 0xAA);
    for (int i = 0; i < 6; i++)
    {
      rcvd_msg[i]= SPI.transfer(0x00);
    }
    S1V30120_send_padding(padding_bytes);
    SPI.endTransaction();
    digitalWrite(S1V30120_CS,HIGH);
    rcvd_tmp = rcvd_msg[3] << 8 | rcvd_msg[2];
    if (rcvd_tmp == expected_message) 
    {
       
       rcvd_tmp = rcvd_msg[5] << 8 | rcvd_msg[4];
       if (rcvd_tmp == expected_result) 
         return 1;
       else
         return 0;
    }
    else 
    return 0;
}
 
void S1V30120_send_padding(unsigned short num_padding_bytes)
{
  for (int i = 0; i < num_padding_bytes; i++)
  {
     SPI.transfer(0x00);
  }
}
 
 
void S1V30120_send_message(volatile char message[], unsigned char message_length)
{
  while(digitalRead(S1V30120_RDY) == 1);  
  digitalWrite(S1V30120_CS,LOW);
  SPI.beginTransaction(SPISettings(750000, MSBFIRST, SPI_MODE3));
  SPI.transfer(0xAA);  
  for (int i = 0; i < message_length; i++)
  {
    SPI.transfer(message[i]);
  }
  SPI.endTransaction();
}
//#########################################################################
 
bool S1V30120_configure_audio(void)
{
  msg_len = 0x0C;
  send_msg[0] = msg_len & 0xFF;          
  send_msg[1] = (msg_len & 0xFF00) >> 8; 
  send_msg[2] = ISC_AUDIO_CONFIG_REQ & 0xFF;
  send_msg[3] = (ISC_AUDIO_CONFIG_REQ & 0xFF00) >> 8;
  send_msg[4] = TTS_AUDIO_CONF_AS;
  send_msg[5] = TTS_AUDIO_CONF_AG;
  send_msg[6] = TTS_AUDIO_CONF_AMP;
  send_msg[7] = TTS_AUDIO_CONF_ASR;
  send_msg[8] = TTS_AUDIO_CONF_AR;
  send_msg[9] = TTS_AUDIO_CONF_ATC;
  send_msg[10] = TTS_AUDIO_CONF_ACS;
  send_msg[11] = TTS_AUDIO_CONF_DC;
  S1V30120_send_message(send_msg, msg_len);
  return S1V30120_parse_response(ISC_AUDIO_CONFIG_RESP, 0x0000, 16);
}
//###########################################################################
bool S1V30120_set_volume(void)
{
  char setvol_code[]={0x06, 0x00, 0x0A, 0x00, 0x00, 0x00};
  S1V30120_send_message(setvol_code, 0x06);
  return S1V30120_parse_response(ISC_AUDIO_VOLUME_RESP, 0x0000, 16);
}
 
bool S1V30120_configure_tts(void)
{
  msg_len = 0x0C;
  send_msg[0] = msg_len & 0xFF;         
  send_msg[1] = (msg_len & 0xFF00) >> 8; 
  send_msg[2] = ISC_TTS_CONFIG_REQ & 0xFF;
  send_msg[3] = (ISC_TTS_CONFIG_REQ & 0xFF00) >> 8;
  send_msg[4] = ISC_TTS_SAMPLE_RATE;
  send_msg[5] = ISC_TTS_VOICE;
  send_msg[6] = ISC_TTS_EPSON_PARSE;
  send_msg[7] = ISC_TTS_LANGUAGE;
  send_msg[8] = ISC_TTS_SPEAK_RATE_LSB;
  send_msg[9] = ISC_TTS_SPEAK_RATE_MSB;
  send_msg[10] = ISC_TTS_DATASOURCE;
  send_msg[11] = 0x00;
  S1V30120_send_message(send_msg, msg_len);
  return S1V30120_parse_response(ISC_TTS_CONFIG_RESP, 0x0000, 16);
}
 
bool S1V30120_speech(String text_to_speech, unsigned char flush_enable)
{
  bool response;
  txt_len = text_to_speech.length();
  msg_len = txt_len + 6;
  send_msg[0] = msg_len & 0xFF;          
  send_msg[1] = (msg_len & 0xFF00) >> 8;
  send_msg[2] = ISC_TTS_SPEAK_REQ & 0xFF;
  send_msg[3] = (ISC_TTS_SPEAK_REQ & 0xFF00) >> 8;
  send_msg[4] = flush_enable; 
  for (int i = 0; i < txt_len; i++)
  {
     send_msg[i+5] = text_to_speech[i];
  }
  send_msg[msg_len-1] = '\0'; 
  S1V30120_send_message(send_msg, msg_len);
  response = S1V30120_parse_response(ISC_TTS_SPEAK_RESP, 0x0000, 16);
  while (!S1V30120_parse_response(ISC_TTS_FINISHED_IND, 0x0000, 16)); 
  return response;
}

Como el manejo del receptor UART que tiene Arduino de manera nativa es muy básico y necesitaba un poco mas de control en la recepción de datos se escribió la rutina para la interrupción en la UART 2 configurando en el Setup los correspondientes registros para su funcionamiento.

Ejemplo extraído del libro "Proyectos con Electrónica".