mbed RPC server for ultrasonic anemometer
Introduction The mbed rapid prototyping board can be a very powerful tool to have for developing and deploying quick applications and solutions without sacrificing power or features found on more expensive prototyping platforms. I’ve been using the mbed boards since the time they were in the beta testing phase, and will continue to use them until I run out of gadgets to control. This particular project uses an mbed to display wind speed, direction vector, and temperature data from a Model 81000 Ultrasonic Anemometer manufactured by R.M. Young Company, with data available from both the serial port and voltage outputs. This anemometer is a 3-axis wind speed sensor capable of measuring velocities up to 40 m/s (90 mph) with direction 0-360 degrees and +_60 degrees elevation. The 81000 will also measure temperature from -50 to +50 degC with a sampling frequency of up to 32 hz. The data is displayed on a 20 character by 4 line LCD display and also made available to remote web users through HTML/RPC commands using TCP/IP protocol. Background The purpose of this project was not to replace the normal data logger used to collect wind speed and temperature data, but to have a simple way of checking the calibration, configuration, and operation of instruments both in the lab, and deployed in the field without having to dedicate a laptop PC for each instrument. System Block Diagram System Electrical Schematic Not all of the features shown on the schematic are used for this project. The extra USB port and PS2 keyboard connections are available for future expansion. I’ve been using this board for several projects, so I left those items connected in case they are needed. I took advantage of as many available software libraries and examples from the mbed.org web site to avoid re-inventing what others have put so much work into already. Some modifications in the serial and LCD display routines were required to interface to my hardware, and I will point out those changes in the following listings. The HTML and RPC web code is still being developed as the project gets closer to be deployed in the field. I’m also considering adding a battery operated WiFi router to the system for remote access. The entire unit will also have to be assembled into a weather-proof enclosure. This is a clip of the HTML page used to read the RPC variables from the mbed and display them for the remote user to evaluate. mbed Software NXP3839 NXP Design Challenge Update 26 Feb 2011
- include "mbed.h"
- include "TextLCD.h"
- include "EthernetNetIf.h"
- include "HTTPServer.h"
- include "HTTPClient.h"
- include "RPCVariable.h"
- include "rpc.h"
DigitalOut led1(LED1, "led1"); DigitalOut led2(LED2, "led2"); DigitalOut led3(LED3, "led3"); DigitalOut led4(LED4, "led4"); void Tx_interrupt(); void Rx_interrupt(); void send_line(); void read_line();
TextLCD lcd(p24,p25,p26,p27,p28,p29,p30,20,4);
Serial pc(USBTX,USBRX); Serial uart(p13,p14); AnalogIn pot18(p18, "pot18"); AnalogIn pot19(p19, "pot19"); AnalogIn pot(p20, "pot"); First create the variables you wish to use float pot18scaled=0.0; float pot19scaled=0.0; float potscaled=0.0; float stemp=0.0; float speedu=0.0; float speedv=0.0; float speedw=0.0; Then attach them to an RPCVariable Object RPCVariable<float> rpc_f1(&pot18scaled, "pot18scaled"); RPCVariable<float> rpc_f2(&pot19scaled, "pot19scaled"); RPCVariable<float> rpc_f3(&potscaled, "potscaled"); RPCVariable<float> rpc_f4(&stemp, "stemp"); RPCVariable<float> rpc_f5(&speedu, "speedu"); RPCVariable<float> rpc_f6(&speedv, "speedv"); RPCVariable<float> rpc_f7(&speedw, "speedw"); LocalFileSystem local("local"); LocalFileSystem fs("webfs"); Circular buffers for serial TX and RX data - used by interrupt routines const int buffer_size = 255; might need to increase buffer size for high baud rates char tx_buffer[buffer_size]; char rx_buffer[buffer_size]; Circular buffer pointers volatile makes read-modify-write atomic volatile int tx_in=0; volatile int tx_out=0; volatile int rx_in=0; volatile int rx_out=0; Line buffers for sprintf and sscanf char tx_line[80]; char rx_line[80];
int main() {
float rx_a=0.0; float rx_b=0.0; float rx_c=0.0; float rx_d=0.0; uart.baud(38400);
Setup a serial interrupt function to receive data uart.attach(&Rx_interrupt, Serial::RxIrq); Setup a serial interrupt function to transmit data uart.attach(&Tx_interrupt, Serial::TxIrq);
Base::add_rpc_class<AnalogIn>(); Base::add_rpc_class<AnalogOut>(); Base::add_rpc_class<DigitalIn>(); Base::add_rpc_class<DigitalOut>(); Base::add_rpc_class<DigitalInOut>();
lcd.reset(); lcd.printf("mbed WebServer V3.4 "); lcd.printf("Entry NXP3839 "); lcd.printf("Build 26 Feb. 2011 "); pc.printf("mbed WebServer V3.4 \n");
EthernetNetIf eth; HTTPServer svr; HTTPClient http;
wait(4); lcd.reset(); lcd.printf("Setting up...\n"); EthernetErr ethErr = eth.setup(); if(ethErr) { lcd.printf("Error %d in setup.\n", ethErr); return -1; } lcd.printf("Setup OK\n");
IpAddr ip = eth.getIp(); lcd.printf("IP: %d.%d.%d.%d\r\n", ip[0], ip[1], ip[2], ip[3]);
FSHandler::mount("/webfs", "/files"); Mount /webfs path on /files web path FSHandler::mount("/webfs", "/"); Mount /webfs path on web root path
svr.addHandler<SimpleHandler>("/hello"); svr.addHandler<RPCHandler>("/rpc"); svr.addHandler<FSHandler>("/files"); svr.addHandler<FSHandler>("/"); Default handler Example : Access to mbed.htm : http://a.b.c.d/mbed.htm or http://a.b.c.d/files/mbed.htm
svr.bind(80); lcd.printf("Listening...\n"); wait(4);
while(1) {
Net::poll(); pot18scaled=(pot18.read()*3.3); pot19scaled=(pot19.read()*3.3); potscaled=(pot.read()*3.3); lcd.locate(0,0); lcd.printf("Requesting UVW Data"); led1 = 1;
uart.putc('M'); remove "" to run uart.putc('A'); remove "" to run uart.putc('!'); remove "" to run wait(1); remove "" to run led1=0; led2=1; Read a line from the large rx buffer from rx interrupt routine if (rx_in != rx_out) remove "" to run { remove "" to run read_line(); remove "" to run Read ASCII number from rx line buffer sscanf(rx_line,"A %f %f %f %f",&rx_a,&rx_b,&rx_c,&rx_d); remove "" to run Print ASCII number to tx line buffer in decimal dummy numbers for testing rx_a=-10.52; rx_b=9.77; rx_c=-2.11; rx_d=23.15; speedu=rx_a; speedv=rx_b; speedw=rx_c; stemp=rx_d; led2 = 0; led1 = 1; lcd.locate(0,1); lcd.printf("%2.2f %2.2f %2.2f ",rx_a,rx_b,rx_c); lcd.locate(0,2); lcd.printf("TempDegC = %2.2f ",rx_d); } remove "" to run led1=0; } } Copy tx line buffer to large tx buffer for tx interrupt routine void send_line() { int i; char temp_char; bool empty; i = 0; Start Critical Section - don't interrupt while changing global buffer variables NVIC_DisableIRQ(UART1_IRQn); empty = (tx_in == tx_out); while ((i==0) || (tx_line[i-1] != '\n')) { Wait if buffer full if (((tx_in + 1) % buffer_size) == tx_out) { End Critical Section - need to let interrupt routine empty buffer by sending NVIC_EnableIRQ(UART1_IRQn); while (((tx_in + 1) % buffer_size) == tx_out) { } Start Critical Section - don't interrupt while changing global buffer variables NVIC_DisableIRQ(UART1_IRQn); } tx_buffer[tx_in] = tx_line[i]; i++; tx_in = (tx_in + 1) % buffer_size; } if (uart.writeable() && (empty)) { temp_char = tx_buffer[tx_out]; tx_out = (tx_out + 1) % buffer_size; Send first character to start tx interrupts, if stopped uart.putc(temp_char); } End Critical Section NVIC_EnableIRQ(UART1_IRQn); return; }
Read a line from the large rx buffer from rx interrupt routine void read_line() { int i; i = 0; Start Critical Section - don't interrupt while changing global buffer variables NVIC_DisableIRQ(UART1_IRQn); Loop reading rx buffer characters until end of line character while ((i==0) || (rx_line[i-1] != '\r')) { Wait if buffer empty if (rx_in == rx_out) { End Critical Section - need to allow rx interrupt to get new characters for buffer NVIC_EnableIRQ(UART1_IRQn); while (rx_in == rx_out) { } Start Critical Section - don't interrupt while changing global buffer variables NVIC_DisableIRQ(UART1_IRQn); } rx_line[i] = rx_buffer[rx_out]; i++; rx_out = (rx_out + 1) % buffer_size; } End Critical Section NVIC_EnableIRQ(UART1_IRQn); rx_line[i-1] = 0; return; }
Interupt Routine to read in data from serial port void Rx_interrupt() { led1=1; Loop just in case more than one character is in UART's receive FIFO buffer Stop if buffer full while ((uart.readable()) && (((rx_in + 1) % buffer_size) != rx_out)) { rx_buffer[rx_in] = uart.getc(); Uncomment to Echo to USB serial to watch data flow monitor_device.putc(rx_buffer[rx_in]); rx_in = (rx_in + 1) % buffer_size; } led1=0; return; }
Interupt Routine to write out data to serial port void Tx_interrupt() { led2=1; Loop to fill more than one character in UART's transmit FIFO buffer Stop if buffer empty while ((uart.writeable()) && (tx_in != tx_out)) { uart.putc(tx_buffer[tx_out]); tx_out = (tx_out + 1) % buffer_size; } led2=0; return; }
HTML Software The web page NXP3839.htm is used to query the mbed for the data from the ultrasonic anemometer and pin voltages used to either monitor the anemometer analog channels, or battery voltage, or some other useful voltage set up in the field. The page uses both javascript and ActiveX, so your browser must have both enabled to view the page correctly. When the timer enable button is clicked, the page will periodically update all variables. The bottom section of the web page lets the user test the TCI/IP and RPC connection by clicking on the red-outlined LEDs from the picture of the mbed and turns them on or off. * Note The “SetIP” function doesn’t do anything yet – the IP of the mbed must be hard-coded * <html> <head> <script language="javascript" type="text/javascript"> <!--
function ReadSTemp() { var xmlhttp1 = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp1.onreadystatechange=function() { if (xmlhttp1.readyState==4 && xmlhttp1.status==200) { form3.STemp.value=xmlhttp1.responseText; } } xmlhttp1.open("GET","http://20.0.0.10/rpc/stemp/read",true); xmlhttp1.send(); }
function ReadSpeedU() { var xmlhttp1 = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp1.onreadystatechange=function() { if (xmlhttp1.readyState==4 && xmlhttp1.status==200) { form3.SpeedU.value=xmlhttp1.responseText; } } xmlhttp1.open("GET","http://20.0.0.10/rpc/speedu/read",true); xmlhttp1.send(); }
function ReadSpeedV() { var xmlhttp1 = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp1.onreadystatechange=function() { if (xmlhttp1.readyState==4 && xmlhttp1.status==200) { form3.SpeedV.value=xmlhttp1.responseText; } } xmlhttp1.open("GET","http://20.0.0.10/rpc/speedv/read",true); xmlhttp1.send(); }
function ReadSpeedW() { var xmlhttp1 = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp1.onreadystatechange=function() { if (xmlhttp1.readyState==4 && xmlhttp1.status==200) { form3.SpeedW.value=xmlhttp1.responseText; } } xmlhttp1.open("GET","http://20.0.0.10/rpc/speedw/read",true); xmlhttp1.send(); }
function ReadVP18() { var xmlhttp1 = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp1.onreadystatechange=function() { if (xmlhttp1.readyState==4 && xmlhttp1.status==200) { form2.VP18.value=xmlhttp1.responseText; } } xmlhttp1.open("GET","http://20.0.0.10/rpc/pot18/read",true); xmlhttp1.send(); }
function ReadVP19() { var xmlhttp1 = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp1.onreadystatechange=function() { if (xmlhttp1.readyState==4 && xmlhttp1.status==200) { form2.VP19.value=xmlhttp1.responseText; } } xmlhttp1.open("GET","http://20.0.0.10/rpc/pot19/read",true); xmlhttp1.send(); }
function ReadVP20() { var xmlhttp1 = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp1.onreadystatechange=function() { if (xmlhttp1.readyState==4 && xmlhttp1.status==200) { form2.VP20.value=xmlhttp1.responseText; } } xmlhttp1.open("GET","http://20.0.0.10/rpc/pot/read",true); xmlhttp1.send(); }
function SetMyIP(IPForm) { var NewIP = IPForm.MyNewIP.value; IPForm.New_IP.value = NewIP; }
function WriteLed1On() { var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp.open("GET","http://20.0.0.10/rpc/led1/write 1",true); xmlhttp.send(); }
function WriteLed2On() { var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp.open("GET","http://20.0.0.10/rpc/led2/write 1",true); xmlhttp.send(); }
function WriteLed3On() { var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp.open("GET","http://20.0.0.10/rpc/led3/write 1",true); xmlhttp.send(); }
function WriteLed4On() { var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp.open("GET","http://20.0.0.10/rpc/led4/write 1",true); xmlhttp.send(); }
function WriteLed1Off() { var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp.open("GET","http://20.0.0.10/rpc/led1/write 0",true); xmlhttp.send(); }
function WriteLed2Off() { var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp.open("GET","http://20.0.0.10/rpc/led2/write 0",true); xmlhttp.send(); }
function WriteLed3Off() { var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp.open("GET","http://20.0.0.10/rpc/led3/write 0",true); xmlhttp.send(); }
function WriteLed4Off() { var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP.3.0"); xmlhttp.open("GET","http://20.0.0.10/rpc/led4/write 0",true); xmlhttp.send(); }
var myTimer;
function endTime() { clearTimeout(myTimer); }
function showTime() { myDate = new Date();
Get time parts hours = myDate.getHours(); minutes = myDate.getMinutes(); seconds = myDate.getSeconds();
Just to display the time in a nice format if (hours < 10) hours = "0" + hours; if (minutes < 10) minutes = "0" + minutes; if (seconds < 10) seconds = "0" + seconds;
actualTime = hours+":"+minutes+":"+seconds; ReadVP18(); ReadVP19(); ReadVP20(); ReadSTemp(); ReadSpeedU(); ReadSpeedV(); ReadSpeedW(); Change the block content document.getElementById("actTime").innerHTML = actualTime; myTimer = setTimeout("showTime()",5000); }
>
</script>
</head> <body style="background:black; color:yellow;">
<center><l1 style="background:black; color: yellow;">NXP3839 mbed Design Challenge Entry - RPC Server</l1></center> <center><l1 style="background:black; color: yellow;">This Page Uses ActiveX functions</l1></center> <center> <table> <tr><td> <center> <P>The actual time is: <span id="actTime"></span></p> <button onclick="showTime();">Start Clock and Timer</button> <button onclick="endTime();">Stop Clock and Timer</button> </center>
<center> <FORM name="form2"> <TABLE BORDER CELLPADDING=3> <TR> <TD><NOBR>Current IP: <INPUT NAME="MyNewIP" VALUE="20.0.0.10" SIZE=15></NOBR></TD> <TD><INPUT TYPE=BUTTON OnClick="SetMyIP(this.form);" VALUE="SetIP"></TD> </TR> <TR> <TD><NOBR>Pin 18 Voltage: <INPUT NAME="VP18" VALUE="Not Read" SIZE=15></NOBR></TD> <TD><INPUT TYPE=BUTTON OnClick="ReadVP18(this.form);" VALUE="Read Pin 18 Voltage"></TD> </TR> <TR> <TD><NOBR>Pin 19 Voltage: <INPUT NAME="VP19" VALUE="Not Read" SIZE=15></NOBR></TD> <TD><INPUT TYPE=BUTTON OnClick="ReadVP19(this.form);" VALUE="Read Pin 19 Voltage"></TD> </TR> <TR> <TD><NOBR>Pin 20 Voltage: <INPUT NAME="VP20" VALUE="Not Read" SIZE=15></NOBR></TD> <TD><INPUT TYPE=BUTTON OnClick="ReadVP20(this.form);" VALUE="Read Pin 20 Voltage"></TD> </TR> </TABLE> </FORM> </center>
<center> <FORM name="form3"> <TABLE BORDER CELLPADDING=3> <TR> <TD><NOBR>Sonic Temperature C: <INPUT NAME="STemp" VALUE="Not Read" SIZE=8></NOBR></TD> <TD><INPUT TYPE=BUTTON OnClick="ReadSTemp(this.form);" VALUE="Read T"></NOBR></TD> </TR> <TR> <TD><NOBR>Wind Speed U (m/s): <INPUT NAME="SpeedU" VALUE="Not Read" SIZE=8></NOBR></TD> <TD><INPUT TYPE=BUTTON OnClick="ReadSpeedU(this.form);" VALUE="Read U"></TD> </TR> <TR> <TD><NOBR>Wind Speed V (m/s): <INPUT NAME="SpeedV" VALUE="Not Read" SIZE=8></NOBR></TD> <TD><INPUT TYPE=BUTTON OnClick="ReadSpeedV(this.form);" VALUE="Read V"></TD> </TR> <TR> <TD><NOBR>Wind Speed W (m/s): <INPUT NAME="SpeedW" VALUE="Not Read" SIZE=8></NOBR></TD> <TD><INPUT TYPE=BUTTON OnClick="ReadSpeedW(this.form);" VALUE="Read W"></TD></TR> </TABLE> </FORM> </center>
<form name="form1" id="form1" method="post" action="http://20.0.0.10/rpc/write" target =" _self"> <center><table border=5> <tr><td><center> <p style="background:black; color: red;"> mbed RPC example: </p>
<img id="_id0:MapImage" src="mbed.jpg" width="192" height="384" alt="mbed LPC2368 development system" usemap="#mbedmapon">
<map id="mbedmapon" name="mbedmapon"> <input type="hidden" name="rpc"> <area shape="rect" coords="27,354,59,368" alt="Turn LED1 On" onclick="WriteLed1On()"> <area shape="rect" coords="62,354,95,368" alt="Turn LED2 On" onclick="WriteLed2On()"> <area shape="rect" coords="98,354,130,368" alt="Turn LED3 On" onclick="WriteLed3On()"> <area shape="rect" coords="133,354,165,368" alt="Turn LED4 On" onclick="WriteLed4On()"> </map></td>
<td><center> <p style="background:black; color: red;"> mbed RPC example: </p>
<img id="_id1:MapImage "src="mbed.jpg" width="192" height="384" alt="mbed LPC2368 development system" usemap="#mbedmapoff">
<map id="mbedmapoff" name="mbedmapoff"> <area shape="rect" onclick="WriteLed1Off()" coords="27,354,59,368" alt="Turn LED1 Off"> <area shape="rect" onclick="WriteLed2Off()" coords="62,354,95,368" alt="Turn LED2 Off"> <area shape="rect" onclick="WriteLed3Off()" coords="98,354,130,368" alt="Turn LED3 Off"> <area shape="rect" onclick="WriteLed4Off()" coords="133,354,165,368" alt="Turn LED4 Off">
</map></td></tr> <tr><td><p style="background:black; color: red;"> Click on LEDS to turn them on: </p></td> <td><p style="background:black; color: red;"> Click on LEDS to turn them off: </p></td></tr></center>
</form> </body> </html> References Mbed rapid prototyping tool: http://mbed.org R.M. Young Company: http://www.youngusa.com/ Model 81000 Ultrasonic Anemometer: http://www.youngusa.com/products/6/3.html Texas Instruments MAX232N Data Sheet: http://www.alldatasheet.com/datasheet-pdf/pdf/27230/TI/MAX232N.html
Please log in to post comments.