streaming server for AM/FM radio via UDP connection.
Dependencies: mbed EthernetNetIf
RadioServer.cpp@2:3b6816fd4ae6, 2012-08-30 (annotated)
- Committer:
- soramimi
- Date:
- Thu Aug 30 08:35:20 2012 +0000
- Revision:
- 2:3b6816fd4ae6
- Parent:
- 1:3357273c97f8
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
soramimi | 0:bd865e5a3df3 | 1 | #include "mbed.h" |
soramimi | 0:bd865e5a3df3 | 2 | #include "EthernetNetIf.h" |
soramimi | 0:bd865e5a3df3 | 3 | #include "UDPSocket.h" |
soramimi | 0:bd865e5a3df3 | 4 | #include "crc32.h" |
soramimi | 0:bd865e5a3df3 | 5 | #include "ns9542.h" |
soramimi | 0:bd865e5a3df3 | 6 | #include <vector> |
soramimi | 0:bd865e5a3df3 | 7 | |
soramimi | 0:bd865e5a3df3 | 8 | DigitalOut led1(LED1); |
soramimi | 0:bd865e5a3df3 | 9 | DigitalOut led2(LED2); |
soramimi | 0:bd865e5a3df3 | 10 | |
soramimi | 0:bd865e5a3df3 | 11 | Ticker ticker; |
soramimi | 0:bd865e5a3df3 | 12 | |
soramimi | 0:bd865e5a3df3 | 13 | SPI spi(p5, p6, p7); // mosi, miso, sclk |
soramimi | 0:bd865e5a3df3 | 14 | DigitalOut spi_cs(p8); |
soramimi | 0:bd865e5a3df3 | 15 | |
soramimi | 2:3b6816fd4ae6 | 16 | EthernetNetIf eth(IpAddr(192,168,0,77), //IP Address |
soramimi | 0:bd865e5a3df3 | 17 | IpAddr(255,255,255,0), //Network Mask |
soramimi | 0:bd865e5a3df3 | 18 | IpAddr(192,168,0,1), //Gateway |
soramimi | 0:bd865e5a3df3 | 19 | IpAddr(192,168,0,1) //DNS |
soramimi | 0:bd865e5a3df3 | 20 | ); |
soramimi | 0:bd865e5a3df3 | 21 | |
soramimi | 0:bd865e5a3df3 | 22 | UDPSocket udpsocket; |
soramimi | 0:bd865e5a3df3 | 23 | |
soramimi | 0:bd865e5a3df3 | 24 | unsigned short sample_l; |
soramimi | 0:bd865e5a3df3 | 25 | unsigned short sample_r; |
soramimi | 0:bd865e5a3df3 | 26 | |
soramimi | 0:bd865e5a3df3 | 27 | unsigned short sample_left() |
soramimi | 0:bd865e5a3df3 | 28 | { |
soramimi | 0:bd865e5a3df3 | 29 | spi_cs = 0; |
soramimi | 0:bd865e5a3df3 | 30 | int v = spi.write(0x6800) & 0x03ff; |
soramimi | 0:bd865e5a3df3 | 31 | spi_cs = 1; |
soramimi | 0:bd865e5a3df3 | 32 | return (v << 6) | (v >> 4); |
soramimi | 0:bd865e5a3df3 | 33 | } |
soramimi | 0:bd865e5a3df3 | 34 | |
soramimi | 0:bd865e5a3df3 | 35 | unsigned short sample_right() |
soramimi | 0:bd865e5a3df3 | 36 | { |
soramimi | 0:bd865e5a3df3 | 37 | spi_cs = 0; |
soramimi | 0:bd865e5a3df3 | 38 | int v = spi.write(0x7800) & 0x03ff; |
soramimi | 0:bd865e5a3df3 | 39 | spi_cs = 1; |
soramimi | 0:bd865e5a3df3 | 40 | return (v << 6) | (v >> 4); |
soramimi | 0:bd865e5a3df3 | 41 | } |
soramimi | 0:bd865e5a3df3 | 42 | |
soramimi | 0:bd865e5a3df3 | 43 | struct host_info_t { |
soramimi | 0:bd865e5a3df3 | 44 | Host host; |
soramimi | 0:bd865e5a3df3 | 45 | int timer; |
soramimi | 0:bd865e5a3df3 | 46 | host_info_t() |
soramimi | 0:bd865e5a3df3 | 47 | { |
soramimi | 0:bd865e5a3df3 | 48 | } |
soramimi | 0:bd865e5a3df3 | 49 | host_info_t(Host h) |
soramimi | 0:bd865e5a3df3 | 50 | : host(h) |
soramimi | 0:bd865e5a3df3 | 51 | , timer(0) |
soramimi | 0:bd865e5a3df3 | 52 | { |
soramimi | 0:bd865e5a3df3 | 53 | } |
soramimi | 0:bd865e5a3df3 | 54 | }; |
soramimi | 0:bd865e5a3df3 | 55 | |
soramimi | 0:bd865e5a3df3 | 56 | std::vector<host_info_t> hostlist; |
soramimi | 0:bd865e5a3df3 | 57 | |
soramimi | 0:bd865e5a3df3 | 58 | void on_udp_socket_event(UDPSocketEvent e) |
soramimi | 0:bd865e5a3df3 | 59 | { |
soramimi | 0:bd865e5a3df3 | 60 | if (e == UDPSOCKET_READABLE) { |
soramimi | 0:bd865e5a3df3 | 61 | char buf[64] = {0}; |
soramimi | 0:bd865e5a3df3 | 62 | Host host; |
soramimi | 0:bd865e5a3df3 | 63 | while (int len = udpsocket.recvfrom(buf, 63, &host)) { |
soramimi | 0:bd865e5a3df3 | 64 | if (len <= 0) { |
soramimi | 0:bd865e5a3df3 | 65 | break; |
soramimi | 0:bd865e5a3df3 | 66 | } |
soramimi | 2:3b6816fd4ae6 | 67 | if (len == 4 && memcmp(buf, "wave", len) == 0) { |
soramimi | 0:bd865e5a3df3 | 68 | __disable_irq(); |
soramimi | 0:bd865e5a3df3 | 69 | size_t i, n; |
soramimi | 0:bd865e5a3df3 | 70 | n = hostlist.size(); |
soramimi | 0:bd865e5a3df3 | 71 | for (i = 0; i < n; i++) { |
soramimi | 0:bd865e5a3df3 | 72 | if (memcmp(&host, &hostlist[i].host, sizeof(Host)) == 0) { |
soramimi | 0:bd865e5a3df3 | 73 | hostlist[i].timer = 0; |
soramimi | 0:bd865e5a3df3 | 74 | break; |
soramimi | 0:bd865e5a3df3 | 75 | } |
soramimi | 0:bd865e5a3df3 | 76 | } |
soramimi | 0:bd865e5a3df3 | 77 | if (i == n) { |
soramimi | 0:bd865e5a3df3 | 78 | hostlist.push_back(host_info_t(host)); |
soramimi | 0:bd865e5a3df3 | 79 | } |
soramimi | 0:bd865e5a3df3 | 80 | __enable_irq(); |
soramimi | 0:bd865e5a3df3 | 81 | continue; |
soramimi | 0:bd865e5a3df3 | 82 | } |
soramimi | 0:bd865e5a3df3 | 83 | if (len > 2 && memcmp(buf, "am", 2) == 0) { |
soramimi | 0:bd865e5a3df3 | 84 | buf[len] = 0; |
soramimi | 0:bd865e5a3df3 | 85 | int f = atoi(buf + 2); |
soramimi | 0:bd865e5a3df3 | 86 | ns9542_tune_am9(f); |
soramimi | 0:bd865e5a3df3 | 87 | continue; |
soramimi | 0:bd865e5a3df3 | 88 | } |
soramimi | 0:bd865e5a3df3 | 89 | if (len > 2 && memcmp(buf, "fm", 2) == 0) { |
soramimi | 0:bd865e5a3df3 | 90 | buf[len] = 0; |
soramimi | 0:bd865e5a3df3 | 91 | int f = atoi(buf + 2); |
soramimi | 0:bd865e5a3df3 | 92 | ns9542_tune_fm(f); |
soramimi | 0:bd865e5a3df3 | 93 | continue; |
soramimi | 0:bd865e5a3df3 | 94 | } |
soramimi | 1:3357273c97f8 | 95 | if (len == 4 && memcmp(buf, "mute", len) == 0) { |
soramimi | 0:bd865e5a3df3 | 96 | ns9542_mute(true); |
soramimi | 0:bd865e5a3df3 | 97 | continue; |
soramimi | 0:bd865e5a3df3 | 98 | } |
soramimi | 0:bd865e5a3df3 | 99 | } |
soramimi | 0:bd865e5a3df3 | 100 | } |
soramimi | 0:bd865e5a3df3 | 101 | } |
soramimi | 0:bd865e5a3df3 | 102 | |
soramimi | 0:bd865e5a3df3 | 103 | static inline void store(unsigned short *p, unsigned short n) |
soramimi | 0:bd865e5a3df3 | 104 | { |
soramimi | 0:bd865e5a3df3 | 105 | ((unsigned char *)p)[0] = n >> 8; |
soramimi | 0:bd865e5a3df3 | 106 | ((unsigned char *)p)[1] = n & 0xff; |
soramimi | 0:bd865e5a3df3 | 107 | } |
soramimi | 0:bd865e5a3df3 | 108 | |
soramimi | 0:bd865e5a3df3 | 109 | static inline void store(unsigned long *p, unsigned long n) |
soramimi | 0:bd865e5a3df3 | 110 | { |
soramimi | 0:bd865e5a3df3 | 111 | ((unsigned char *)p)[0] = n >> 24; |
soramimi | 0:bd865e5a3df3 | 112 | ((unsigned char *)p)[1] = n >> 16; |
soramimi | 0:bd865e5a3df3 | 113 | ((unsigned char *)p)[2] = n >> 8; |
soramimi | 0:bd865e5a3df3 | 114 | ((unsigned char *)p)[3] = n & 0xff; |
soramimi | 0:bd865e5a3df3 | 115 | } |
soramimi | 0:bd865e5a3df3 | 116 | |
soramimi | 0:bd865e5a3df3 | 117 | class Sampler { |
soramimi | 0:bd865e5a3df3 | 118 | private: |
soramimi | 0:bd865e5a3df3 | 119 | unsigned long buffer_a[257]; |
soramimi | 0:bd865e5a3df3 | 120 | unsigned long buffer_b[257]; |
soramimi | 0:bd865e5a3df3 | 121 | unsigned long *in_ptr; |
soramimi | 0:bd865e5a3df3 | 122 | unsigned long *out_ptr; |
soramimi | 0:bd865e5a3df3 | 123 | bool output_available; |
soramimi | 0:bd865e5a3df3 | 124 | unsigned long crc; |
soramimi | 0:bd865e5a3df3 | 125 | int offset; |
soramimi | 0:bd865e5a3df3 | 126 | public: |
soramimi | 0:bd865e5a3df3 | 127 | Sampler() |
soramimi | 0:bd865e5a3df3 | 128 | { |
soramimi | 0:bd865e5a3df3 | 129 | in_ptr = buffer_a; |
soramimi | 0:bd865e5a3df3 | 130 | out_ptr = buffer_b; |
soramimi | 0:bd865e5a3df3 | 131 | output_available = false; |
soramimi | 0:bd865e5a3df3 | 132 | offset = 0; |
soramimi | 0:bd865e5a3df3 | 133 | crc = 0; |
soramimi | 0:bd865e5a3df3 | 134 | } |
soramimi | 0:bd865e5a3df3 | 135 | |
soramimi | 0:bd865e5a3df3 | 136 | void on_tick() |
soramimi | 0:bd865e5a3df3 | 137 | { |
soramimi | 0:bd865e5a3df3 | 138 | if (output_available) { |
soramimi | 0:bd865e5a3df3 | 139 | return; |
soramimi | 0:bd865e5a3df3 | 140 | } |
soramimi | 0:bd865e5a3df3 | 141 | unsigned short *p = (unsigned short *)in_ptr; |
soramimi | 0:bd865e5a3df3 | 142 | p += offset * 2; |
soramimi | 0:bd865e5a3df3 | 143 | store(p + 0, sample_l); |
soramimi | 0:bd865e5a3df3 | 144 | store(p + 1, sample_r); |
soramimi | 0:bd865e5a3df3 | 145 | crc = crc32(crc, (unsigned char *)p, 4); |
soramimi | 0:bd865e5a3df3 | 146 | offset++; |
soramimi | 0:bd865e5a3df3 | 147 | if (offset >= 256) { |
soramimi | 0:bd865e5a3df3 | 148 | std::swap(in_ptr, out_ptr); |
soramimi | 0:bd865e5a3df3 | 149 | store(&out_ptr[256], crc); |
soramimi | 0:bd865e5a3df3 | 150 | output_available = true; |
soramimi | 0:bd865e5a3df3 | 151 | offset = 0; |
soramimi | 0:bd865e5a3df3 | 152 | crc = 0; |
soramimi | 0:bd865e5a3df3 | 153 | } |
soramimi | 0:bd865e5a3df3 | 154 | } |
soramimi | 0:bd865e5a3df3 | 155 | |
soramimi | 0:bd865e5a3df3 | 156 | char const *output_buffer() |
soramimi | 0:bd865e5a3df3 | 157 | { |
soramimi | 0:bd865e5a3df3 | 158 | return output_available ? (char const *)out_ptr : 0; |
soramimi | 0:bd865e5a3df3 | 159 | } |
soramimi | 0:bd865e5a3df3 | 160 | |
soramimi | 0:bd865e5a3df3 | 161 | void output_done() |
soramimi | 0:bd865e5a3df3 | 162 | { |
soramimi | 0:bd865e5a3df3 | 163 | output_available = false; |
soramimi | 0:bd865e5a3df3 | 164 | } |
soramimi | 0:bd865e5a3df3 | 165 | }; |
soramimi | 0:bd865e5a3df3 | 166 | |
soramimi | 0:bd865e5a3df3 | 167 | Sampler sampler; |
soramimi | 0:bd865e5a3df3 | 168 | |
soramimi | 0:bd865e5a3df3 | 169 | void on_tick() |
soramimi | 0:bd865e5a3df3 | 170 | { |
soramimi | 0:bd865e5a3df3 | 171 | sampler.on_tick(); |
soramimi | 0:bd865e5a3df3 | 172 | } |
soramimi | 0:bd865e5a3df3 | 173 | |
soramimi | 0:bd865e5a3df3 | 174 | int main() |
soramimi | 0:bd865e5a3df3 | 175 | { |
soramimi | 0:bd865e5a3df3 | 176 | spi_cs = 1; |
soramimi | 0:bd865e5a3df3 | 177 | spi.format(16, 0); |
soramimi | 0:bd865e5a3df3 | 178 | spi.frequency(3000000); |
soramimi | 0:bd865e5a3df3 | 179 | |
soramimi | 0:bd865e5a3df3 | 180 | ns9542_init(); |
soramimi | 0:bd865e5a3df3 | 181 | |
soramimi | 0:bd865e5a3df3 | 182 | eth.setup(); |
soramimi | 0:bd865e5a3df3 | 183 | Host host(IpAddr(), 2000); |
soramimi | 0:bd865e5a3df3 | 184 | udpsocket.bind(host); |
soramimi | 0:bd865e5a3df3 | 185 | udpsocket.setOnEvent(&on_udp_socket_event); |
soramimi | 0:bd865e5a3df3 | 186 | |
soramimi | 0:bd865e5a3df3 | 187 | led1 = 0; |
soramimi | 0:bd865e5a3df3 | 188 | ticker.attach(&on_tick, 1.0 / 32000); |
soramimi | 0:bd865e5a3df3 | 189 | |
soramimi | 0:bd865e5a3df3 | 190 | int led1_timer = 0; |
soramimi | 0:bd865e5a3df3 | 191 | |
soramimi | 0:bd865e5a3df3 | 192 | while (1) { |
soramimi | 0:bd865e5a3df3 | 193 | sample_l = sample_left(); |
soramimi | 0:bd865e5a3df3 | 194 | sample_r = sample_right(); |
soramimi | 0:bd865e5a3df3 | 195 | |
soramimi | 0:bd865e5a3df3 | 196 | Net::poll(); |
soramimi | 0:bd865e5a3df3 | 197 | |
soramimi | 0:bd865e5a3df3 | 198 | char const *p = sampler.output_buffer(); |
soramimi | 0:bd865e5a3df3 | 199 | if (p) { |
soramimi | 0:bd865e5a3df3 | 200 | for (std::vector<host_info_t>::iterator it = hostlist.begin(); it != hostlist.end(); it++) { |
soramimi | 0:bd865e5a3df3 | 201 | udpsocket.sendto(p, 1028, (Host *)&it->host); |
soramimi | 0:bd865e5a3df3 | 202 | } |
soramimi | 0:bd865e5a3df3 | 203 | sampler.output_done(); |
soramimi | 0:bd865e5a3df3 | 204 | |
soramimi | 0:bd865e5a3df3 | 205 | __disable_irq(); |
soramimi | 0:bd865e5a3df3 | 206 | int i = hostlist.size(); |
soramimi | 0:bd865e5a3df3 | 207 | while (i > 0) { |
soramimi | 0:bd865e5a3df3 | 208 | i--; |
soramimi | 0:bd865e5a3df3 | 209 | hostlist[i].timer++; |
soramimi | 0:bd865e5a3df3 | 210 | if (hostlist[i].timer > 125) { |
soramimi | 0:bd865e5a3df3 | 211 | hostlist.erase(hostlist.begin() + i); |
soramimi | 0:bd865e5a3df3 | 212 | } |
soramimi | 0:bd865e5a3df3 | 213 | } |
soramimi | 0:bd865e5a3df3 | 214 | __enable_irq(); |
soramimi | 0:bd865e5a3df3 | 215 | |
soramimi | 0:bd865e5a3df3 | 216 | led1_timer++; |
soramimi | 0:bd865e5a3df3 | 217 | if (led1_timer >= 125) { |
soramimi | 0:bd865e5a3df3 | 218 | led1 = !led1; |
soramimi | 0:bd865e5a3df3 | 219 | led1_timer = 0; |
soramimi | 0:bd865e5a3df3 | 220 | } |
soramimi | 0:bd865e5a3df3 | 221 | } |
soramimi | 0:bd865e5a3df3 | 222 | } |
soramimi | 0:bd865e5a3df3 | 223 | |
soramimi | 0:bd865e5a3df3 | 224 | return 0; |
soramimi | 0:bd865e5a3df3 | 225 | } |