The London Hackspace Bandwidth meter, now works over ethernet.

Dependencies:   EthernetInterface LPD8806 Tiny-HTTPD mbed-rtos mbed BonjourLib

Committer:
Jasper
Date:
Fri May 30 09:45:45 2014 +0000
Revision:
4:d522cfa6e410
Parent:
3:f4018fcd1ce8
commiting for some reason?

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jasper 0:0e7057b49904 1 #include "mbed.h"
Jasper 0:0e7057b49904 2 #include "EthernetInterface.h"
Jasper 0:0e7057b49904 3 #include "rtos.h"
Jasper 0:0e7057b49904 4 #include "vfd.h"
Jasper 0:0e7057b49904 5 #include "LPD8806.h"
Jasper 0:0e7057b49904 6 #include "HTTPD.h"
Jasper 2:49025fae1e1f 7 #include "mDNSResponder.h"
Jasper 2:49025fae1e1f 8 #include "lwip/inet.h"
Jasper 2:49025fae1e1f 9 #include "lwip/netif.h"
Jasper 0:0e7057b49904 10
Jasper 0:0e7057b49904 11 DigitalOut myled(LED1);
Jasper 0:0e7057b49904 12 DigitalOut led2(LED2);
Jasper 0:0e7057b49904 13 DigitalOut led3(LED3);
Jasper 0:0e7057b49904 14
Jasper 0:0e7057b49904 15 LPD8806 strip = LPD8806(32);
Jasper 0:0e7057b49904 16 EthernetInterface eth;
Jasper 0:0e7057b49904 17 HTTPD *httpd;
Jasper 0:0e7057b49904 18
Jasper 0:0e7057b49904 19 //LocalFileSystem local("local");
Jasper 0:0e7057b49904 20
Jasper 0:0e7057b49904 21 void strip_clear() {
Jasper 0:0e7057b49904 22 int i;
Jasper 0:0e7057b49904 23
Jasper 0:0e7057b49904 24 for (i = 0 ; i < strip.numPixels() ; i++) {
Jasper 0:0e7057b49904 25 // clear the strip
Jasper 0:0e7057b49904 26 strip.setPixelColor(i, strip.Color(0, 0, 0));
Jasper 0:0e7057b49904 27 }
Jasper 0:0e7057b49904 28 }
Jasper 0:0e7057b49904 29
Jasper 0:0e7057b49904 30 void setPixelsTop(int start, int end, int colour) {
Jasper 0:0e7057b49904 31 int i;
Jasper 0:0e7057b49904 32
Jasper 0:0e7057b49904 33 for (i = start; i < end + 1 ; i++) {
Jasper 0:0e7057b49904 34 strip.setPixelColor(i, colour);
Jasper 0:0e7057b49904 35 }
Jasper 0:0e7057b49904 36 }
Jasper 0:0e7057b49904 37
Jasper 0:0e7057b49904 38 void setPixelsBottom(int start, int end, int colour) {
Jasper 0:0e7057b49904 39 int i;
Jasper 0:0e7057b49904 40
Jasper 0:0e7057b49904 41 for (i = start; i < end + 1 ; i++) {
Jasper 0:0e7057b49904 42 strip.setPixelColor(16 + i, colour);
Jasper 0:0e7057b49904 43 }
Jasper 0:0e7057b49904 44 }
Jasper 0:0e7057b49904 45
Jasper 0:0e7057b49904 46 /* 0 - 16 */
Jasper 0:0e7057b49904 47 void top_strip(int quantity) {
Jasper 0:0e7057b49904 48 if (quantity < 16) {
Jasper 0:0e7057b49904 49 // blank unused bits.
Jasper 0:0e7057b49904 50 setPixelsTop(quantity, 15, 0);
Jasper 0:0e7057b49904 51 }
Jasper 0:0e7057b49904 52
Jasper 0:0e7057b49904 53 if (quantity == 0) return;
Jasper 0:0e7057b49904 54
Jasper 0:0e7057b49904 55 quantity --;
Jasper 0:0e7057b49904 56
Jasper 0:0e7057b49904 57 setPixelsTop(0, quantity < 12 ? quantity : 11, strip.Color(0, 127, 0));
Jasper 0:0e7057b49904 58
Jasper 0:0e7057b49904 59 if (quantity > 11)
Jasper 0:0e7057b49904 60 setPixelsTop(12, quantity < 14 ? quantity : 14, strip.Color(127, 127, 0));
Jasper 0:0e7057b49904 61
Jasper 0:0e7057b49904 62 if (quantity > 13)
Jasper 0:0e7057b49904 63 setPixelsTop(14, quantity < 16 ? quantity : 16, strip.Color(127, 0, 0));
Jasper 0:0e7057b49904 64 }
Jasper 0:0e7057b49904 65
Jasper 0:0e7057b49904 66 void bottom_strip(int quantity) {
Jasper 0:0e7057b49904 67 if (quantity < 16) {
Jasper 0:0e7057b49904 68 // blank unused bits.
Jasper 0:0e7057b49904 69 setPixelsBottom(quantity, 15, 0);
Jasper 0:0e7057b49904 70 }
Jasper 0:0e7057b49904 71
Jasper 0:0e7057b49904 72 if (quantity == 0) return;
Jasper 0:0e7057b49904 73 quantity --;
Jasper 0:0e7057b49904 74
Jasper 0:0e7057b49904 75 setPixelsBottom(0, quantity < 12 ? quantity : 11, strip.Color(0, 127, 0));
Jasper 0:0e7057b49904 76
Jasper 0:0e7057b49904 77 if (quantity > 11)
Jasper 0:0e7057b49904 78 setPixelsBottom(12, quantity < 14 ? quantity : 14, strip.Color(127, 127, 0));
Jasper 0:0e7057b49904 79
Jasper 0:0e7057b49904 80 if (quantity > 13)
Jasper 0:0e7057b49904 81 setPixelsBottom(14, quantity < 16 ? quantity : 16, strip.Color(127, 0, 0));
Jasper 0:0e7057b49904 82 }
Jasper 0:0e7057b49904 83
Jasper 0:0e7057b49904 84 void emf_blue() {
Jasper 0:0e7057b49904 85 setPixelsBottom(0, 15, strip.Color(0, 161, 228));
Jasper 0:0e7057b49904 86 setPixelsTop(0, 15, strip.Color(0, 161, 228));
Jasper 0:0e7057b49904 87 }
Jasper 0:0e7057b49904 88
Jasper 0:0e7057b49904 89 const static char *ok = "Ok";
Jasper 0:0e7057b49904 90 const static char *fail = "Fail";
Jasper 0:0e7057b49904 91
Jasper 0:0e7057b49904 92 const char* const index_page =
Jasper 3:f4018fcd1ce8 93 "<html><head>"
Jasper 3:f4018fcd1ce8 94 "<title>LHS Bandwidth Meter</title>"
Jasper 3:f4018fcd1ce8 95 "<LINK REL=\"SHORTCUT ICON\" HREF=\"/favicon.ico\">"
Jasper 3:f4018fcd1ce8 96 "</head><body>"
Jasper 0:0e7057b49904 97 "<h1>Bandwidth Meter</h1>"
Jasper 0:0e7057b49904 98 "<h1>Commands:</h1>"
Jasper 0:0e7057b49904 99
Jasper 0:0e7057b49904 100 "<h2>Led Strips:</h2>"
Jasper 0:0e7057b49904 101 "<ul>"
Jasper 0:0e7057b49904 102 "<li>/s/clear - clear the led strips</li>"
Jasper 0:0e7057b49904 103 "<li>/s/t?<i>amount</i> - set the top strip to <i>amount</i> (0-16).</li>"
Jasper 0:0e7057b49904 104 "<li>/s/b?<i>amount</i> - set the bottom strip to <i>amount</i> (0-16).</li>"
Jasper 0:0e7057b49904 105 "<li>/s/set?&lt;t,b&gt;hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh - set the LEDs in the top ('t') or bottom ('b') strip to hhh to colours.</li>"
Jasper 0:0e7057b49904 106 "</ul>"
Jasper 0:0e7057b49904 107 "<p><a href=\"/s/clear\">clear</a></p>"
Jasper 0:0e7057b49904 108
Jasper 0:0e7057b49904 109 "<h2>VFD</h2>"
Jasper 0:0e7057b49904 110 "<ul>"
Jasper 0:0e7057b49904 111 "<li>/v/r - reset the vfd.</li>"
Jasper 0:0e7057b49904 112 "<li>/v/c - clear the vfd.</li>"
Jasper 0:0e7057b49904 113 "<li>/v/p?<i>x,y</i> - set the cursor position to <i>x,y</i>. 0,0 is top left.</li>"
Jasper 0:0e7057b49904 114 "<li>/v/w?<i>text</i> - write <i>text</i> at the current cursor position</li>"
Jasper 0:0e7057b49904 115 "</ul>"
Jasper 0:0e7057b49904 116
Jasper 0:0e7057b49904 117 "</body>"
Jasper 0:0e7057b49904 118 "</html>";
Jasper 2:49025fae1e1f 119
Jasper 2:49025fae1e1f 120 const char* const favicon =
Jasper 2:49025fae1e1f 121 "\x00\x00\x01\x00\x01\x00\x10\x10\x02\x00\x01\x00\x01\x00\xb0\x00\x00\x00"
Jasper 2:49025fae1e1f 122 "\x16\x00\x00\x00(\x00\x00\x00\x10\x00\x00\x00 \x00\x00\x00\x01\x00\x01"
Jasper 2:49025fae1e1f 123 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
Jasper 2:49025fae1e1f 124 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\xf0\xf0\x00"
Jasper 2:49025fae1e1f 125 "\x00\xf0\xf0\x00\x00\xf0\xf0\x00\x00\xf0\xf0\x00\x00\x0f\x0f\x00\x00\x0f"
Jasper 2:49025fae1e1f 126 "\x0f\x00\x00\x0f\x0f\x00\x00\x0f\x0f\x00\x00\xf0\xf0\x00\x00\xf0\xf0\x00"
Jasper 2:49025fae1e1f 127 "\x00\xf0\xf0\x00\x00\xf0\xf0\x00\x00\x0f\x0f\x00\x00\x0f\x0f\x00\x00\x0f"
Jasper 2:49025fae1e1f 128 "\x0f\x00\x00\x0f\x0f\x00\x00\xf0\x0f\x00\x00\xc0\x03\x00\x00\x80\x01\x00"
Jasper 2:49025fae1e1f 129 "\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
Jasper 2:49025fae1e1f 130 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
Jasper 2:49025fae1e1f 131 "\x00\x80\x01\x00\x00\x80\x01\x00\x00\xc0\x03\x00\x00\xf0\x0f\x00\x00";
Jasper 2:49025fae1e1f 132
Jasper 0:0e7057b49904 133 const char* const text_plain = "Content-Type: text/plain\r\n";
Jasper 0:0e7057b49904 134
Jasper 0:0e7057b49904 135 void callback_static (int id) {
Jasper 0:0e7057b49904 136 char buf[256];
Jasper 0:0e7057b49904 137
Jasper 0:0e7057b49904 138 strcpy(buf, httpd->getFilename(id));
Jasper 0:0e7057b49904 139 printf("static %d %s\r\n", id, buf);
Jasper 0:0e7057b49904 140
Jasper 0:0e7057b49904 141 if (strcmp(buf, "") == 0 || strcmp(buf, "index.html") == 0) {
Jasper 0:0e7057b49904 142 httpd->send(id, index_page, strlen(index_page), "Content-Type: text/html\r\n");
Jasper 2:49025fae1e1f 143 } else if (strcmp(buf, "favicon.ico") == 0) {
Jasper 2:49025fae1e1f 144 httpd->send(id, favicon, 198, "image/vnd.microsoft.icon\r\n");
Jasper 0:0e7057b49904 145 } else {
Jasper 0:0e7057b49904 146 httpd->httpdError(id, 404);
Jasper 0:0e7057b49904 147 }
Jasper 0:0e7057b49904 148 }
Jasper 0:0e7057b49904 149
Jasper 0:0e7057b49904 150 void s_clear(int id) {
Jasper 0:0e7057b49904 151 httpd->send(id, ok, strlen(ok), text_plain);
Jasper 0:0e7057b49904 152 strip_clear();
Jasper 0:0e7057b49904 153 strip.show();
Jasper 0:0e7057b49904 154 }
Jasper 0:0e7057b49904 155
Jasper 0:0e7057b49904 156 void s_top(int id) {
Jasper 0:0e7057b49904 157 int q;
Jasper 0:0e7057b49904 158
Jasper 0:0e7057b49904 159 q = atoi(httpd->getQueryString(id));
Jasper 0:0e7057b49904 160 if (q < 0 || q > 16) {
Jasper 0:0e7057b49904 161 httpd->send(id, fail, strlen(fail), text_plain);
Jasper 0:0e7057b49904 162 return;
Jasper 0:0e7057b49904 163 }
Jasper 0:0e7057b49904 164
Jasper 0:0e7057b49904 165 httpd->send(id, ok, strlen(ok), text_plain);
Jasper 0:0e7057b49904 166 top_strip(q);
Jasper 0:0e7057b49904 167 strip.show();
Jasper 0:0e7057b49904 168 }
Jasper 0:0e7057b49904 169
Jasper 0:0e7057b49904 170 void s_bottom(int id) {
Jasper 0:0e7057b49904 171 int q;
Jasper 0:0e7057b49904 172
Jasper 0:0e7057b49904 173 q = atoi(httpd->getQueryString(id));
Jasper 0:0e7057b49904 174 if (q < 0 || q > 16) {
Jasper 0:0e7057b49904 175 httpd->send(id, fail, strlen(fail), text_plain);
Jasper 0:0e7057b49904 176 return;
Jasper 0:0e7057b49904 177 }
Jasper 0:0e7057b49904 178
Jasper 0:0e7057b49904 179 httpd->send(id, ok, strlen(ok), text_plain);
Jasper 0:0e7057b49904 180 bottom_strip(q);
Jasper 0:0e7057b49904 181 strip.show();
Jasper 0:0e7057b49904 182 }
Jasper 0:0e7057b49904 183
Jasper 0:0e7057b49904 184 int hex_char_to_int(char h) {
Jasper 0:0e7057b49904 185 int ret;
Jasper 0:0e7057b49904 186 if (h >= '0' || h <= '9') {
Jasper 0:0e7057b49904 187 ret = h - 48;
Jasper 0:0e7057b49904 188 } else if (h >= 'A' || h <= 'F') {
Jasper 0:0e7057b49904 189 ret = (h - 'A') + 10;
Jasper 0:0e7057b49904 190 } else if (h >= 'a' || h <= 'f') {
Jasper 0:0e7057b49904 191 ret = (h - 'a') + 10;
Jasper 0:0e7057b49904 192 }
Jasper 0:0e7057b49904 193 return ret;
Jasper 0:0e7057b49904 194 }
Jasper 0:0e7057b49904 195
Jasper 0:0e7057b49904 196 void s_set(int id) {
Jasper 0:0e7057b49904 197 int p, r, g, b, poff=0;
Jasper 0:0e7057b49904 198 const char* buf;
Jasper 0:0e7057b49904 199
Jasper 0:0e7057b49904 200 buf = httpd->getQueryString(id);
Jasper 0:0e7057b49904 201
Jasper 0:0e7057b49904 202 if (strlen(buf) != 49) {
Jasper 0:0e7057b49904 203 httpd->send(id, fail, strlen(fail), text_plain);
Jasper 0:0e7057b49904 204 }
Jasper 0:0e7057b49904 205
Jasper 0:0e7057b49904 206 httpd->send(id, ok, strlen(ok), text_plain);
Jasper 0:0e7057b49904 207
Jasper 0:0e7057b49904 208 if (buf[0] == 'b') {
Jasper 0:0e7057b49904 209 poff = 16;
Jasper 0:0e7057b49904 210 }
Jasper 0:0e7057b49904 211
Jasper 0:0e7057b49904 212 for (p = 0; p < 16 ; p ++) {
Jasper 0:0e7057b49904 213 r = hex_char_to_int(buf[(p * 3) + 1]);
Jasper 0:0e7057b49904 214 g = hex_char_to_int(buf[(p * 3) + 2]);
Jasper 0:0e7057b49904 215 b = hex_char_to_int(buf[(p * 3) + 3]);
Jasper 0:0e7057b49904 216 strip.setPixelColor(p + poff , strip.Color(r,g,b));
Jasper 0:0e7057b49904 217 }
Jasper 0:0e7057b49904 218
Jasper 0:0e7057b49904 219 strip.show();
Jasper 0:0e7057b49904 220 }
Jasper 0:0e7057b49904 221
Jasper 0:0e7057b49904 222 void s_emf(int id) {
Jasper 0:0e7057b49904 223 emf_blue();
Jasper 0:0e7057b49904 224 strip.show();
Jasper 0:0e7057b49904 225 }
Jasper 0:0e7057b49904 226
Jasper 0:0e7057b49904 227 void v_reset(int id) {
Jasper 0:0e7057b49904 228 httpd->send(id, ok, strlen(ok), text_plain);
Jasper 0:0e7057b49904 229 vfd_reset();
Jasper 0:0e7057b49904 230 }
Jasper 0:0e7057b49904 231
Jasper 0:0e7057b49904 232 void v_clear(int id) {
Jasper 0:0e7057b49904 233 httpd->send(id, ok, strlen(ok), text_plain);
Jasper 0:0e7057b49904 234 vfd_init();
Jasper 0:0e7057b49904 235 }
Jasper 0:0e7057b49904 236
Jasper 0:0e7057b49904 237 void v_write(int id) {
Jasper 0:0e7057b49904 238 char buf[128];
Jasper 0:0e7057b49904 239
Jasper 0:0e7057b49904 240 if (strlen(httpd->getQueryString(id)) > 127) {
Jasper 0:0e7057b49904 241 httpd->send(id, fail, strlen(fail), text_plain);
Jasper 0:0e7057b49904 242 return;
Jasper 0:0e7057b49904 243 }
Jasper 0:0e7057b49904 244
Jasper 0:0e7057b49904 245 httpd->send(id, ok, strlen(ok), text_plain);
Jasper 0:0e7057b49904 246 httpd->urldecode(httpd->getQueryString(id), buf, 128);
Jasper 0:0e7057b49904 247 send_text(buf);
Jasper 0:0e7057b49904 248 }
Jasper 0:0e7057b49904 249
Jasper 0:0e7057b49904 250 void v_position(int id) {
Jasper 0:0e7057b49904 251 unsigned int v,h;
Jasper 0:0e7057b49904 252
Jasper 0:0e7057b49904 253 httpd->send(id, ok, strlen(ok), text_plain);
Jasper 0:0e7057b49904 254
Jasper 0:0e7057b49904 255 sscanf(httpd->getQueryString(id), "%d,%d", &h,&v);
Jasper 0:0e7057b49904 256 if (v > 3) v = 0;
Jasper 0:0e7057b49904 257 if (h > 19) h = 0;
Jasper 0:0e7057b49904 258 vfd_pos(h, v);
Jasper 0:0e7057b49904 259 }
Jasper 0:0e7057b49904 260
Jasper 0:0e7057b49904 261 void v_logo(int id) {
Jasper 0:0e7057b49904 262 vfd_init();
Jasper 0:0e7057b49904 263 logo();
Jasper 0:0e7057b49904 264 }
Jasper 0:0e7057b49904 265
Jasper 2:49025fae1e1f 266 mDNSResponder mdns;
Jasper 2:49025fae1e1f 267
Jasper 2:49025fae1e1f 268 void mdns_thread(void const *args) {
Jasper 2:49025fae1e1f 269 ip_addr_t ip, *z;
Jasper 2:49025fae1e1f 270 z = (ip_addr_t *)args;
Jasper 2:49025fae1e1f 271
Jasper 2:49025fae1e1f 272 ip.addr = z->addr;
Jasper 2:49025fae1e1f 273
Jasper 2:49025fae1e1f 274 mdns.announce(ip, "net-o-meter", "_http._tcp", 80, "LHS Bandwidth Meter",
Jasper 2:49025fae1e1f 275 (char *[]) { // NULL terminated list of KV's = see
Jasper 2:49025fae1e1f 276 "path=/", // http://www.zeroconf.org/Rendezvous/txtrecords.html
Jasper 2:49025fae1e1f 277 NULL
Jasper 2:49025fae1e1f 278 }
Jasper 2:49025fae1e1f 279 );
Jasper 2:49025fae1e1f 280 }
Jasper 2:49025fae1e1f 281
Jasper 0:0e7057b49904 282 int main() {
Jasper 2:49025fae1e1f 283 ip_addr_t ip;
Jasper 0:0e7057b49904 284
Jasper 0:0e7057b49904 285 led3 = 1;
Jasper 0:0e7057b49904 286
Jasper 0:0e7057b49904 287 printf("Hello World\n\r");
Jasper 0:0e7057b49904 288
Jasper 0:0e7057b49904 289 wait_ms(10);
Jasper 0:0e7057b49904 290 vfd_reset();
Jasper 0:0e7057b49904 291 wait_ms(10);
Jasper 0:0e7057b49904 292 vfd_init();
Jasper 0:0e7057b49904 293 wait_ms(10);
Jasper 0:0e7057b49904 294 logo();
Jasper 0:0e7057b49904 295
Jasper 0:0e7057b49904 296 strip.begin();
Jasper 0:0e7057b49904 297 strip_clear();
Jasper 0:0e7057b49904 298 strip.show();
Jasper 0:0e7057b49904 299
Jasper 0:0e7057b49904 300 eth.init(); //Use DHCP
Jasper 0:0e7057b49904 301 printf("Mac address is %s\n\r", eth.getMACAddress());
Jasper 0:0e7057b49904 302 eth.connect();
Jasper 0:0e7057b49904 303 printf("IP Address is %s\n\r", eth.getIPAddress());
Jasper 0:0e7057b49904 304
Jasper 2:49025fae1e1f 305 inet_aton(eth.getIPAddress(), &ip);
Jasper 2:49025fae1e1f 306
Jasper 2:49025fae1e1f 307 Thread thread(mdns_thread, &ip);
Jasper 2:49025fae1e1f 308
Jasper 0:0e7057b49904 309 httpd = new HTTPD;
Jasper 0:0e7057b49904 310
Jasper 0:0e7057b49904 311 led2 = 1;
Jasper 0:0e7057b49904 312
Jasper 0:0e7057b49904 313 // LED strips
Jasper 0:0e7057b49904 314 httpd->attach("/s/t", &s_top);
Jasper 0:0e7057b49904 315 httpd->attach("/s/b", &s_bottom);
Jasper 0:0e7057b49904 316 httpd->attach("/s/clear", &s_clear);
Jasper 0:0e7057b49904 317 httpd->attach("/s/s", &s_set);
Jasper 0:0e7057b49904 318 httpd->attach("/s/e", &s_emf);
Jasper 0:0e7057b49904 319
Jasper 0:0e7057b49904 320 // VFD
Jasper 0:0e7057b49904 321 httpd->attach("/v/r", &v_reset);
Jasper 0:0e7057b49904 322 httpd->attach("/v/c", &v_clear);
Jasper 0:0e7057b49904 323 httpd->attach("/v/w", &v_write);
Jasper 0:0e7057b49904 324 httpd->attach("/v/p", &v_position);
Jasper 0:0e7057b49904 325 httpd->attach("/v/l", &v_logo);
Jasper 0:0e7057b49904 326
Jasper 0:0e7057b49904 327 // httpd->attach("/local/", "/local/");
Jasper 0:0e7057b49904 328
Jasper 0:0e7057b49904 329 httpd->attach("/", &callback_static);
Jasper 0:0e7057b49904 330 httpd->start(80);
Jasper 0:0e7057b49904 331 printf("httpd ready\r\n");
Jasper 0:0e7057b49904 332
Jasper 0:0e7057b49904 333 for (;;) {
Jasper 0:0e7057b49904 334 httpd->poll();
Jasper 0:0e7057b49904 335 if (myled == 0) {
Jasper 0:0e7057b49904 336 myled = 1;
Jasper 0:0e7057b49904 337 } else {
Jasper 0:0e7057b49904 338 myled = 0;
Jasper 0:0e7057b49904 339 }
Jasper 0:0e7057b49904 340 }
Jasper 0:0e7057b49904 341 }