A Network Text-To-Speech Voice Announcer
which allows any networked UDP nodes to send text messages for speaking
by this SAM TTS node.
A small and neat self-contained 'dual-core'
device can be made using a Wemos dual base adapter, allowing two D1
Mini's to sit side by side.
Only the SAM Speaker firmware needs to use gpio2 for its speaker output, so the Annex gpio2 is ignored.
And SAM's gpio0 Busy output signal can conveniently be interconnected directly to the Annex gpio0 Busy interrupt input - so no wire is even needed.
We cannot prevent ESP8266 devices from outputting serial debug info at bootup.
So rather than feed SAM RX input from our Annex TX output (which would generate unwanted bootup noise) it is better to send to SAM RX using Annex serial2 gpio5.
This (purple) wire link is the only actual wiring mod needed on the dual base adapter, and allows us to send only those messages we wish to speak, when we want it to speak.
It also leaves the Annex hardware serial free to be used as a voice test
input - whereby any text sent from a serial console connected to the
Annex USB is received on the hardware serial port and passed straight
out through software serial2 gpio5 to be read on SAM RX and spoken as audio
output on SAM gpio2.
It uses gpio0 as a Busy signal from SAM so it only sends the next message from the queue when SAM signals it is no longer busy speaking. The
Annex Network Node module uses
EasyNet to receive UDP text messages sent to it by
other network nodes, which it then
forwards over serial2 to be spoken by the SAM Speaker module.
The EasyNet message format is: TargetNodeName Instruction optional data (name and instruction are not case sensitive, data remains as-is).
This application uses nodename SAM to recognise the instruction SAY followed by "the message string within guotes"
The node name is easily changed, and so can instructions names in the local instructionslist$ if their corresponding branch is similarly renamed.
In this case, SAM will also recognise the instruction IP which will cause it to branch to the IP subroutine to announce its local IP address.
It will also recognise and respond to the REPLY instruction, eg: ALL REPLY will cause EasyNet nodes to return a response message.So it
is not actually essential to address the node by its unique node
name, because EasyNet nodes will also respond to the collective name of
"All", or any of the names that are present in their Groupname" (which can contain multiple group names), or it will also respond to its local IP address.
To test the dual device for correct operation, use the Toolkit UDP console and send a message, eg: (don't forget to ensure the UDP console is set to the correct subnet and port) All Say "Hello world" or SAM SAY "Hi, I am Sam"
or SAM SAY "Overheating" or All Say "Movement outside"
Any networked nodes can then send similar
appropriate messages whenever their sensors are triggered by something
which needs to be verbally announced.
Basic:
'SAM Voice Announcer - Networked Text 2 Speech using EasyNet and
Circular FIFO queue for SAM Speaker TTS firmware installed on a 2nd
module nodename$ = "SAM"
groupname$ = "Voice\Announcer\Speakers\TTS\Speech" localIP$ = WORD$(IP$,1) netIP$ = WORD$(localIP$,1,".") + "." + WORD$(localIP$,2,".") + "." + WORD$(localIP$,3,".") + "." nodeIP$ = WORD$(localIP$,4,".") udpport = 5001 'change to suit your own preference, but don't forget to do the same for all nodes if nodename$ = "" then nodename$ = "Node" + nodeIP$ udp.begin(udpport) onudp udpRX udpmsg$ = "" 'variable to hold incoming UDP message instructionslist$ = ucase$("Reply Say IP ") 'List of Subdir branches available to action as remote instructions instruction$ = "" buffersize = 20 'needs to be one more than the max message queue size dim q$(buffersize) qitem$ = "" 'queue data variable qsize = 0 'size of queue qfront = 0 qback = 0 serial.mode 115200 'Hardware serial serial2.mode 115200, 5, 4 'Software serial led_pin = 13 'Optional Busy/Ready status LED pin.mode led_pin, output ready = 0 'Ready state of Busy pin signal busy_pin = 0 'modules hardware Busy signal pin.mode busy_pin, input, pullup interrupt busy_pin, busy onserial serialin qitem$ = "SAM, Text 2 Speech Voice Announcer." gosub say 'pause 3000 gosub IP timer0 2000, dQ wait say: qpush gosub dQ return IP: qitem$ = word$(ip$,1) qitem$ = "This I P address is " + replace$(qitem$,"."," dot ") gosub say return dQ: 'if pin(busy_pin) = ready then qitem$ = "" if qsize > 0 then qpull if qitem$ <> "" then print2 qitem$ endif 'endif return udpRX: udpmsg$ = udp.read$ target$ = ucase$(word$(udpmsg$,1)) 'Target is always first word of message - may be NodeName or GroupName or "ALL" or localIP addr if (target$=localIP$) OR (target$=ucase$(nodename$)) OR (target$=ucase$(groupname$)) OR (instr(ucase$(groupname$),target$)>0) OR (target$="ALL") then '"Target Name or IP matched" instruction$ = trim$(ucase$(word$(udpmsg$,2))) 'Instruction is always second word of message wlog udpmsg$ if word.find(ucase$(instructionslist$),instruction$) > 0 then 'check if a recognised as valid on local instructionslist$ qitem$ = word$(udpmsg$,2,|"|) gosub instruction$ 'branch to action the corresponding instruction subroutine else udp.reply udpmsg$ + " INSTRUCTION NOT RECOGNISED" endif 'word.find instruction$ endif return serialin: print2 serial.input$ return busy: if pin(busy_pin) = ready then pin(led_pin) = 0 gosub dQ else pin(led_pin) = 1 endif return sub qpush if qsize + 1 >= buffersize then print2 "ERROR: Queue is full" end else q$(qback) = qitem$ ' Push item to back of q qback = (qback + 1) mod buffersize ' Adjust the back pointer endif qsize = (qback - qfront) mod buffersize end sub sub qpull if qsize < 1 then print2 "ERROR: Queue is empty" end else qitem$ = q$(qfront) ' Pull item from front of q qfront = (qfront + 1) mod (buffersize) ' Adjust the front pointer endif qsize = (qback - qfront) mod buffersize end sub REPLY: udp.reply "Reply from " + Nodename$ return ---------------- end ----------------- HINT:
SAM translates English text into audio speech, plus it understands four punctuation marks: the hyphen (-), comma (,), period (.), and question mark (?).
The hyphen (-) serves to mark clause boundaries by inserting a short pause in the speech. The
comma marks phrase boundaries and inserts a longer pause approximately double
that of the hyphen.
The period adds a pause and also causes the pitch to fall. The question-mark also adds a pause, but it causes the pitch to
rise.
You can sprinkle those punctuation marks liberally in your text to make it pronounce better and sound more to your liking.
|