├── P8T.p8 ├── .gitattributes ├── twitter.js ├── twitter.php ├── main.js ├── p8-gpio-comms.js └── index.html /P8T.p8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seleb/pico-8-gpio/HEAD/P8T.p8 -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /twitter.js: -------------------------------------------------------------------------------- 1 | var twitter={ 2 | tweets:null, 3 | 4 | getTweet: function(params){ 5 | var request = new XMLHttpRequest(); 6 | request.onreadystatechange = function(){ 7 | if(request.readyState == 4){ 8 | if(request.status == 200){ 9 | // success 10 | this.tweets = JSON.parse(request.responseText); 11 | console.log("Loaded tweet:",JSON.stringify(this.tweets)); 12 | 13 | var t = this.tweets.statuses[0]; 14 | 15 | this.ontweetloaded(); 16 | }else if(request.status == 400){ 17 | // error 18 | console.error(request.responseText); 19 | }else{ 20 | // something else 21 | } 22 | } 23 | }.bind(this); 24 | 25 | request.open("get", "https://seans.site/stuff/P8T/twitter.php"+params, true); 26 | request.send(); 27 | }, 28 | 29 | ontweetloaded: function(){ 30 | // can overwrite this 31 | } 32 | }; -------------------------------------------------------------------------------- /twitter.php: -------------------------------------------------------------------------------- 1 | array( 10 | "method" => "POST", 11 | "header" => "Authorization: Basic ".$credentials."\r\n". 12 | "Content-type: application/x-www-form-urlencoded;charset=UTF-8\r\n", 13 | "content" => "grant_type=client_credentials" 14 | ) 15 | )); 16 | $authResponse = file_get_contents("https://api.twitter.com/oauth2/token", false, $authContext); 17 | $decodedAuth = json_decode($authResponse, true); 18 | $bearerToken = $decodedAuth["access_token"]; 19 | 20 | 21 | // use token to get data 22 | $context = stream_context_create(array( 23 | "http" => array( 24 | "method" => "GET", 25 | "header" => "Authorization: Bearer " . $bearerToken . "\r\n" 26 | ) 27 | )); 28 | 29 | $encodedData = file_get_contents("https://api.twitter.com/1.1/search/tweets.json?".$_SERVER['QUERY_STRING'], false, $context); 30 | 31 | echo $encodedData; 32 | 33 | ?> 34 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | 2 | var last=[]; 3 | var btns=[]; 4 | var btnsp=[]; 5 | var searchField = document.getElementById("searchField"); 6 | 7 | function search(params){ 8 | if(comms.state == comms.States.IDLE){ 9 | twitter.getTweet.bind(twitter)(params); 10 | comms.state = comms.States.HOLD; 11 | }else{ 12 | console.error("Comms are busy; action ignored"); 13 | } 14 | } 15 | 16 | 17 | function next(){ 18 | if(twitter.tweets != null){ 19 | last.push(twitter.tweets.search_metadata.max_id_str); 20 | search(twitter.tweets.search_metadata.next_results); 21 | } 22 | } 23 | 24 | function prev(){ 25 | if(last.length > 0){ 26 | search("?max_id="+last.pop()+"&q="+encodeURIComponent(searchField.value)+"&count=1"); 27 | }else{ 28 | refresh(); 29 | } 30 | } 31 | 32 | function refresh(){ 33 | last=[]; 34 | search("?q="+encodeURIComponent(searchField.value)+"&count=1"); 35 | } 36 | 37 | comms.onstartup = function(){ 38 | comms.onreceive = function(){ 39 | var btn = comms.incoming_packet[0]; 40 | var b = []; 41 | 42 | for(i=0;i<6;++i){ 43 | b[i] = (btn & (1 << i)) != 0; 44 | btnsp[i] = !btns[i] && b[i]; 45 | btns[i]=b[i]; 46 | } 47 | if(btnsp[0]){ 48 | prev(); 49 | }else if(btnsp[1]){ 50 | next(); 51 | }else if(btnsp[2]){ 52 | refresh(); 53 | } 54 | }; 55 | twitter.ontweetloaded = function(){ 56 | try{ 57 | var t = twitter.tweets.statuses[0]; 58 | comms.send( 59 | t.user.name+" (@"+t.user.screen_name+"):\n\n" 60 | +t.text+"\n\n" 61 | +new Date(t.created_at).toLocaleDateString() 62 | ); 63 | }catch(e){ 64 | comms.send(":( Sorry! :(\nThere was an error retrieving tweets.\n\nTry refreshing the page if this continues."); 65 | } 66 | }; 67 | 68 | search("?q="+encodeURIComponent(searchField.value)+"&count=1"); 69 | }; -------------------------------------------------------------------------------- /p8-gpio-comms.js: -------------------------------------------------------------------------------- 1 | var pico8_gpio = new Array(128); 2 | 3 | var comms = { 4 | Turns:Object.freeze({ 5 | MY_TURN:1, 6 | THEIR_TURN:0 7 | }), 8 | States:Object.freeze({ 9 | STARTING:0, 10 | RECEIVING:1, 11 | SENDING:2, 12 | SENT:3, 13 | RECEIVED:4, 14 | IDLE:5 15 | }), 16 | Pins:Object.freeze({ 17 | TURN:0, 18 | STATE:1, 19 | FRAME_ID:2, 20 | FRAME_LENGTH:3, 21 | DATA_LENGTH:4, 22 | DEBUG:5, 23 | DATA_START:6 24 | }), 25 | 26 | debug:0, 27 | state:0, 28 | 29 | char_offset: 31, // ignore control characters 30 | data_bytes: 120, // bytes available for data 31 | 32 | pending_packets: [], 33 | incoming_packet: [], 34 | 35 | send: function (data){ 36 | if(this.state == this.States.IDLE || this.state == this.States.HOLD){ 37 | // how many packets do we need to send to fit the data? 38 | var num_packets = Math.max(1,Math.ceil(data.length/this.data_bytes)); 39 | 40 | // split the data into packets 41 | this.pending_packets = []; 42 | for(i = 1; i <= num_packets; ++i){ 43 | // grab the first chunk of the data as a character array 44 | var chars = data.substr(0,this.data_bytes).split(""); 45 | 46 | // convert characters to numbers matching 47 | for(j=0;j 3 | 4 | 5 | 6 | P8T PICO-8 Cartridge 7 | 8 | 9 | 81 | 82 | 83 | 84 |
85 |
86 | 87 | 88 | 89 | 90 | 91 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 157 | 158 |
Reset Reset
Pause Pause
Fullscreen Fullscreen
Toggle Sound Sound
159 |
160 | 161 | --------------------------------------------------------------------------------