Skip to content

Commit 0daf6da

Browse files
First
1 parent 8c24e7f commit 0daf6da

16 files changed

+55854
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
## Testing
1+
## ESPFLIX: A free video streaming service that runs on a $1 microcontroller.
22

espflix.ino

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
/* Copyright (c) 2020, Peter Barrett
2+
**
3+
** Permission to use, copy, modify, and/or distribute this software for
4+
** any purpose with or without fee is hereby granted, provided that the
5+
** above copyright notice and this permission notice appear in all copies.
6+
**
7+
** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
8+
** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
9+
** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
10+
** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
11+
** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
12+
** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OpR OTHER TORTIOUS ACTION,
13+
** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
14+
** SOFTWARE.
15+
*/
16+
17+
#include "esp_wifi.h"
18+
#include "tcpip_adapter.h"
19+
#include "esp_event_loop.h"
20+
#include "esp_event.h"
21+
#include "esp_system.h"
22+
#include "esp_int_wdt.h"
23+
#include "esp_task_wdt.h"
24+
#include "soc/rtc.h"
25+
#include "freertos/task.h"
26+
#include "driver/i2s.h"
27+
#include "nvs_flash.h"
28+
#include "nvs.h"
29+
30+
#define PERF
31+
#include "src/streamer.h"
32+
#include "src/player.h"
33+
#include "src/video.h"
34+
35+
#include <map>
36+
#include <string>
37+
using namespace std;
38+
39+
//================================================================================
40+
//================================================================================
41+
// Audio
42+
// Single pin with second order software PDM modulation
43+
// see https://www.sigma-delta.de/sdopt/index.html for the tool used to design the Delta Sigma modulator
44+
// 48000hz sample rate, 32x oversampling for PDM
45+
// Generous 192k bitrate, low complexity, low latency SBC codec
46+
47+
void audio_init_hw()
48+
{
49+
mem("audio_init_hw");
50+
pinMode(IR_PIN,INPUT);
51+
52+
i2s_config_t i2s_config;
53+
i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);
54+
i2s_config.sample_rate = 48000;
55+
i2s_config.bits_per_sample = (i2s_bits_per_sample_t)16,
56+
i2s_config.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
57+
i2s_config.communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S);
58+
i2s_config.dma_buf_count = 2;
59+
i2s_config.dma_buf_len = 512; // 2k * 2 bytes for audio buffers, pretty tight
60+
i2s_config.use_apll = false;
61+
i2s_config.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1;
62+
i2s_driver_install((i2s_port_t)1, &i2s_config, 0, NULL);
63+
mem("after audio_init_hw");
64+
65+
i2s_pin_config_t pin_config;
66+
pin_config.bck_io_num = I2S_PIN_NO_CHANGE;
67+
pin_config.ws_io_num = I2S_PIN_NO_CHANGE;
68+
pin_config.data_out_num = 18;
69+
pin_config.data_in_num = I2S_PIN_NO_CHANGE;
70+
i2s_set_pin((i2s_port_t)1, &pin_config);
71+
}
72+
73+
void pdm_second_order(uint16_t* dst, const int16_t* src, int len, int32_t a1 = 0x7FFF*1.18940, int a2 = 0x7FFF*2.12340)
74+
{
75+
static int32_t _i0;
76+
static int32_t _i1;
77+
static int32_t _i2;
78+
int32_t i0 = _i0; // force compiler to use registers
79+
int32_t i1 = _i1;
80+
int32_t i2 = _i2;
81+
82+
uint32_t b = 0;
83+
int32_t s = 0;
84+
len <<= 1;
85+
while (len--)
86+
{
87+
if (len & 1)
88+
s = *src++ * 2;
89+
i0 = (i0 + s) >> 1; // lopass
90+
int n = 16;
91+
while (n--) {
92+
b <<= 1;
93+
if (i2 >= 0) {
94+
i1 += i0 - a1 - (i2 >> 7); // feedback
95+
i2 += i1 - a2;
96+
b |= 1;
97+
} else {
98+
i1 += i0 + a1 - (i2 >> 7); // feedback
99+
i2 += i1 + a2;
100+
}
101+
}
102+
*dst++ = b;
103+
}
104+
_i0 = i0;
105+
_i1 = i1;
106+
_i2 = i2;
107+
}
108+
109+
const int16_t _sin32[32] = {
110+
0x0000,0xE708,0xCF05,0xB8E4,0xA57F,0x9594,0x89C0,0x8277,
111+
0x8001,0x8277,0x89C0,0x9594,0xA57F,0xB8E4,0xCF05,0xE708,
112+
0x0000,0x18F8,0x30FB,0x471C,0x5A81,0x6A6C,0x7640,0x7D89,
113+
0x7FFF,0x7D89,0x7640,0x6A6C,0x5A81,0x471C,0x30FB,0x18F8,
114+
};
115+
116+
uint8_t _beep;
117+
void beep()
118+
{
119+
_beep = 5;
120+
}
121+
122+
// this routine turns PCM into PDM. Also handles generating slience and beeps
123+
void write_pcm_16(const int16_t* s, int n, int channels)
124+
{
125+
uint16_t samples_data[128*2];
126+
127+
PLOG(PDM_START);
128+
if (_beep) {
129+
int16_t* b = (int16_t*)samples_data + 128; // both src and dst buffer to save stack
130+
for (int i = 0; i < 128; i++)
131+
b[i] = _sin32[i & 31] >> 2; // sinewave beep plz
132+
_beep--;
133+
pdm_second_order(samples_data,b,128);
134+
} else {
135+
if (s)
136+
pdm_second_order(samples_data,s,n);
137+
else {
138+
for (int i = 0; i < 128*2; i++)
139+
samples_data[i] = 0xAAAA; // PDM silence
140+
}
141+
}
142+
size_t i2s_bytes_write;
143+
i2s_write((i2s_port_t)1, samples_data, sizeof(samples_data), &i2s_bytes_write, portMAX_DELAY); // audio thread will block here
144+
PLOG(PDM_END);
145+
}
146+
147+
//================================================================================
148+
//================================================================================
149+
// Store the pts of the current movie in non-volatile storage
150+
151+
nvs_handle _nvs;
152+
static int nv_open()
153+
{
154+
if (_nvs || (nvs_open("espflix", NVS_READWRITE, &_nvs) == 0))
155+
return 0;
156+
return -1;
157+
}
158+
159+
// force key to max 15 chars
160+
static const char* limit_key(const char* key)
161+
{
162+
int n = strlen(key);
163+
return (n < 15) ? key : key + (n-15);
164+
}
165+
166+
int64_t nv_read(const char* key)
167+
{
168+
int64_t pts = 0;
169+
if (nv_open() == 0)
170+
nvs_get_i64(_nvs,limit_key(key),&pts);
171+
return pts;
172+
}
173+
174+
void nv_write(const char* key, int64_t pts)
175+
{
176+
if (nv_open() == 0)
177+
nvs_set_i64(_nvs,limit_key(key),pts);
178+
}
179+
180+
//================================================================================
181+
//================================================================================
182+
// WIFI
183+
184+
extern EventGroupHandle_t _event_group;
185+
WiFiState _wifi_state = NONE;
186+
WiFiState wifi_state()
187+
{
188+
return _wifi_state;
189+
}
190+
191+
std::map<string,int> _ssids;
192+
std::map<string,int>& wifi_list()
193+
{
194+
return _ssids;
195+
}
196+
197+
// manually entered ssid/password
198+
void wifi_join(const char* ssid, const char* pwd)
199+
{
200+
wifi_config_t wifi_config = {0};
201+
strcpy((char*)wifi_config.sta.ssid, ssid);
202+
strcpy((char*)wifi_config.sta.password, pwd);
203+
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);
204+
esp_wifi_connect();
205+
_wifi_state = CONNECTING;
206+
}
207+
208+
// current ssid if any
209+
std::string wifi_ssid()
210+
{
211+
wifi_config_t wifi_config = {0};
212+
esp_wifi_get_config(ESP_IF_WIFI_STA,&wifi_config);
213+
return (char*)wifi_config.sta.ssid;
214+
}
215+
216+
void wifi_scan()
217+
{
218+
wifi_scan_config_t config = {0};
219+
config.scan_type = WIFI_SCAN_TYPE_ACTIVE;
220+
esp_wifi_scan_start(&config,false);
221+
_wifi_state = SCANNING;
222+
}
223+
224+
void wifi_disconnect()
225+
{
226+
esp_wifi_disconnect();
227+
}
228+
229+
static esp_err_t event_handler(void *ctx, system_event_t *event)
230+
{
231+
printf("EVENT %d\n",event->event_id);
232+
switch(event->event_id)
233+
{
234+
case SYSTEM_EVENT_STA_START:
235+
printf("SYSTEM_EVENT_STA_START\n");
236+
esp_wifi_connect();
237+
break;
238+
239+
case SYSTEM_EVENT_STA_GOT_IP:
240+
tcpip_adapter_ip_info_t ip_info;
241+
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
242+
printf("Local IP Address: %s\n", ip4addr_ntoa(&ip_info.ip));
243+
_ssids.clear();
244+
_wifi_state = CONNECTED;
245+
break;
246+
247+
case SYSTEM_EVENT_STA_DISCONNECTED:
248+
printf("SYSTEM_EVENT_STA_DISCONNECTED %d\n",event->event_info.disconnected.reason);
249+
wifi_scan();
250+
break;
251+
252+
case SYSTEM_EVENT_SCAN_DONE:
253+
{
254+
uint16_t apCount = 0;
255+
esp_wifi_scan_get_ap_num(&apCount);
256+
printf("SYSTEM_EVENT_SCAN_DONE %d %d\n",apCount,sizeof(wifi_ap_record_t)*apCount);
257+
if (apCount > 16)
258+
apCount = 16;
259+
wifi_ap_record_t list[16]; // careful of large stack 16*80 bytes. Limitation of the 16 highest power APs
260+
esp_wifi_scan_get_ap_records(&apCount, list);
261+
_ssids.clear();
262+
for (uint16_t i = 0; i < apCount; i++) {
263+
wifi_ap_record_t *ap = list + i;
264+
const char* ssid = (const char *)ap->ssid;
265+
if (_ssids.find(ssid) == _ssids.end()) {
266+
_ssids[ssid] = (ap->rssi << 8) | ap->authmode;
267+
//printf("\"%s\",%d,\n",ssid,(ap->rssi << 8) | ap->authmode);
268+
}
269+
}
270+
}
271+
_wifi_state = SCAN_COMPLETE;
272+
break;
273+
}
274+
return ESP_OK;
275+
}
276+
277+
void init_wifi()
278+
{
279+
esp_log_level_set("wifi", ESP_LOG_VERBOSE);
280+
tcpip_adapter_init();
281+
esp_event_loop_init(event_handler, NULL);
282+
283+
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
284+
cfg.static_tx_buf_num = 8;
285+
cfg.static_rx_buf_num = 16;
286+
esp_wifi_init(&cfg);
287+
esp_wifi_set_mode(WIFI_MODE_STA);
288+
esp_wifi_start();
289+
printf("connecting to %s\n",wifi_ssid().c_str());
290+
291+
mem("after esp_wifi_start");
292+
//wifi_join("failure","test");
293+
}
294+
295+
//================================================================================
296+
//================================================================================
297+
// Decide on a video standard
298+
299+
#define PAL 0
300+
#define NTSC 1
301+
302+
void setup()
303+
{
304+
rtc_clk_cpu_freq_set(RTC_CPU_FREQ_240M);
305+
_event_group = xEventGroupCreate();
306+
mem("setup");
307+
init_wifi();
308+
}
309+
310+
// this loop always runs on app_core (1).
311+
void loop()
312+
{
313+
espflix_run(1); // never returns
314+
}

0 commit comments

Comments
 (0)