Worked out the spooky problems I was having with the background high score updating process. The basic problem was me recommending doing it in the Post Vsync Callback, er, don't do that. Instead, use a custom WaitVsync(). Every frame(assuming your game makes it every frame, otherwise anything you do is dangerous anyways) the main program spends all free cycles after game logic/etc simply checking the vsync flag to start processing a new frame. Instead we can use some of those wasted free cycles to do the updating. In very time tight games we could only do this update when the game logic wasn't too heavy that frame, but I don't have a need for that right now.
Code: Select all
inline void SWaitVsync(uint8_t frames){
UpdatePad();
// if(TCNT1 < 1000UL)
UpdateUzenet();
WaitVsync(frames);
}
That is all I am doing, and all the "impossible" things that were happening disappeared. This is much better since it should be able to fit into cycle tight games better now. I am again at the limits of the emulator, but it seems I have the Uzebox side of things close to done. The high score server unfortunately needs to be redesigned because I see a process that is easier for the Uzebox(which the overriding goal is to be easier on Uzebox side, even if harder on server). I do expect under normal network conditions that Solitaire will retrieve all current world records before the Uzebox logo at the start is even done. After that, it repeatedly checks the eeprom for new scores and sends any there, then retrieves the list again in case anything new has happened. I do expect a high score you just entered to get to the server and read back in the updated list before the high score screen displays world records a couple seconds later. To give you an idea of what should be pretty close to what you'd need to include:
Code: Select all
void UpdateUzenet(){
//SPrintNum(3,0,uzenet_step,1);
//SPrintNum(3,1,uzenet_wait_ticks,1);
//SPrintNum(6,3,TCNT1,1);
if(uzenet_wait_ticks)
if(--uzenet_wait_ticks)
return;//still waiting for something, try again next tick
//SPrintNum(5,20,uzenet_step,0);
//SPrintNum(5,21,uzenet_wait_ticks,1);
if(uzenet_step == 0){//RESET MODULE//////////////////////////////////////////////////////////////////////////////////////////////
UBRR0H=0;
UCSR0A=(1<<U2X0); // double speed mode
UCSR0C=(1<<UCSZ01)+(1<<UCSZ00)+(0<<USBS0); //8-bit frame, no parity, 1 stop bit
UCSR0B=(1<<RXEN0)+(1<<TXEN0); //Enable UART TX & RX
DDRD|=(1<<PD3);
PORTD&=~(1<<PD3);//need to turn this pin back on next frame
InitUartTxBuffer();
InitUartRxBuffer();
uzenet_substate = 0;
uzenet_wait_ticks = 1;
uzenet_wait2 = 0;
uzenet_iteration = 0;
uzenet_step++;
}else if(uzenet_step == 1){//TURN MODULE BACK ON(RESET PIN)//////////////////////////////////////////////////////////////////////
PORTD |= (1<<PD3);
uzenet_wait_ticks = 180;//give it a chance to spit out its bootup stuff
uzenet_step++;
uzenet_iteration = sizeof(BaudLookUpTable)+1;//counter for number of attempts(each at different baud)
}else if(uzenet_step == 2){//FIND THE BAUD RATE//////////////////////////////////////////////////////////////////////////////////
uzenet_step++;return;//EMULATOR HACK////////////////////////////////////
if(uzenet_substate == 0){//attempt to reach ESP8266 on UART
if(uzenet_wait2 >= uzenet_iteration)//tried every baud rate, something is wrong
uzenet_step = 0;
else{
InitUartRxBuffer();//clear any garbage(from startup or bad baud rates comms)
UBRR0L = pgm_read_byte(BaudLookUpTable[uzenet_wait2++]);//try a new baud rate
uzenet_wait_ticks = 2;//make sure a response could be here in time(next tick)
Uzenet_SendStringP(PSTR("AT\r\n"));//we might have sent garbage at the last baud rate, clear it(could get "ERROR\r\n")
uzenet_substate++;//move on to next sub-step
}
}else if(uzenet_substate == 1){//CLEAR POSSIBLE BAD DATA ON MODULE SIDE(MAY HAVE SENT AT WRONG RATE BEFORE)/////////////////
Uzenet_SendStringP(PSTR("AT\r\n"));
uzenet_substate++;
uzenet_wait_ticks = 8;
uzenet_wait2 = 5;
}else if(uzenet_substate == 2){//CHECK FOR "OK\r\n" TO VERIFY CORRECT BAUD//////////////////////////////////////////////////
//we cleared our buffer last step, so if we don't get "OK\r\n" there was a comms problem
if(UartUnreadCount() < 4 || UartReadChar() != 'O' || UartReadChar() != 'K'){//wrong baud or other comms problem, keep trying
if(--uzenet_wait2 == 0)//took too long, start from the top
uzenet_step = 0;
else
uzenet_wait_ticks = 1;
}else{//WE FOUND THE BAUD RATE!!
uzenet_step++;//uzenet_wait_ticks is 0 so will automatically run next step on the next frame
uzenet_wait2 = 0;
uzenet_iteration = 0;
}
}
}else if(uzenet_step == 3){//turn off echo
Uzenet_SendStringP("ATE0\r\n");
uzenet_wait_ticks = 1;
uzenet_step++;
}else if(uzenet_step == 4){//CHANGE 8266 BAUD RATE TO THE ONE WE WANT////////////////////////////////////////////////////////////
Uzenet_SendStringP(PSTR("AT+CIOBAUD=57600\r\n"));
UBRR0L = 30; //57600 bauds 5760 bytes/s 96 bytes/field
uzenet_step++;//uzenet_wait_ticks is 0 so will automatically run next step on the next frame
}else if(uzenet_step == 5){//VERIFY WIFI CONNECTION//////////////////////////////////////////////////////////////////////////////
//the response from baud rate change should already be in Rx buffer
InitUartRxBuffer();//we will ignore it and check errors later(should have worked though)
Uzenet_SendStringP(PSTR("AT+CWJAP\r\n"));
uzenet_wait2 = 255;
uzenet_step++;
}else if(uzenet_step == 6){//WAIT FOR WIFI CONNECTION OK MESSAGE/////////////////////////////////////////////////////////////////
if(UartUnreadCount() > 1 && UartReadChar() == 'O' && UartReadChar() == 'K'){//connected
uzenet_step++;
// uzenet_iteration = 0;
}else if(--uzenet_wait2 == 0)//timed out, reset and try from the top
uzenet_step = 0;
else
uzenet_wait_ticks = 1;
}else if(uzenet_step == 7){//SET SINGLE CONNECTION MODE//////////////////////////////////////////////////////////////////////////
InitUartRxBuffer();//burn any bytes left from connecting to wifi
Uzenet_SendStringP(PSTR("AT+CIPMUX=0\r\n"));//this should always work if we got this far
uzenet_wait_ticks = 1;
uzenet_step++;
uzenet_iteration = 0;
}else if(uzenet_step == 8){//CONNECT TO UZENET///////////////////////////////////////////////////////////////////////////////////
InitUartRxBuffer();//burn response from previous command(which should always work)
//Uzenet_SendStringP(PSTR("AT+CIPSTART=\"TCP\",\"uzebox.net\""));//we need to split this message to support Tx buffer sized as low as 8
Uzenet_SendStringP(PSTR("AT+CIPSTART=0,\"TCP\",\"uzebox.net\",51697\r\n"));//EMULATOR HACK////////////////////////////////////
uzenet_wait_ticks = 2;//wait for '>'
uzenet_wait2 = 255;//wait for response in next step
uzenet_step++;
}else if(uzenet_step == 9){//VERIFY CONNECTION IS GOOD(check response)///////////////////////////////////////////////////////////
if(UartUnreadCount() > 1 && UartReadChar() == 'O' && UartReadChar() == 'K')//connection good, ready to send packets
uzenet_step++;
else if(--uzenet_wait2 == 0)//count down to try again
uzenet_step = 0;//something went wrong, try again from the start
}else if(uzenet_step == 10){//PREPARE PACKET SEND///////////////////////////////////////////////////////////////////////////////
Uzenet_SendStringP("AT+CIPSEND=0,7,\r\n");//EMULATOR HACK////////////////////////////////////
uzenet_iteration = 0;
uzenet_wait_ticks = 2;//give time to receive '>'
uzenet_wait2 = 4;
uzenet_substate = 0;
uzenet_step++;
}else if(uzenet_step == 11){//BUILD A REQUEST PACKET///////////////////////////////////////////////////////////////////////////
Uzenet_SendByte(0xC7);//magic number
Uzenet_SendByte(1);//set request params(so we can ask for things later with less bytes)
Uzenet_SendByte(0x00);//flow control flags
Uzenet_SendByte(SOLITAIRE_UZENET_ID>>8);//rom ID
Uzenet_SendByte(SOLITAIRE_UZENET_ID&0xFF);
Uzenet_SendStringP(PSTR("\r\n"));
uzenet_wait_ticks = 2;
}else if(uzenet_step == 12){
//should already have got '>', otherwise we will detect problems later anyway(would be hardware problem)
if(uzenet_substate == 0){//check if this score should be sent using wait2 as sub-state to save ram
if(eeprom_data[uzenet_iteration*10] & 128){//already sent this score before
if(++uzenet_iteration > 2){//we have cycled through all the entries
uzenet_iteration = 0;
uzenet_step++;//now retrieve updated high scores from the server
uzenet_substate = 1;
uzenet_wait_ticks = 2;
}
}else//move on to next sub-step
uzenet_substate++;
}else if(uzenet_substate == 1){
Uzenet_SendStringP(PSTR("AT+CIPSEND=0,12\r\n"));//EMULATOR HACK////////////////////////////////////
uzenet_wait_ticks = 2;//wait for '>'
uzenet_substate++;
}else if(uzenet_substate == 2){//BUILD A REQUEST PACKET
Uzenet_SendByte(0xC7);//magic number so server knows this isn't run away code
Uzenet_SendByte(2);//write command
Uzenet_SendByte(eeprom_data[(uzenet_iteration*10)+0]);//send score part including MSB
Uzenet_SendByte(eeprom_data[(uzenet_iteration*10)+1]);//server sorts everything by MSB so names are irrelevent(older score precedence)
uzenet_substate++;
}else if(uzenet_substate == 3){
for(uint8_t i=2;i<10;i++)//send name bytes(after score because server sorts everything by MSB)
Uzenet_SendByte(eeprom_data[(uzenet_iteration*10)+i]&127);//don't send the MSB which is used as a flag for different things
Uzenet_SendStringP(PSTR("\r\n"));
uzenet_wait_ticks = 2;
uzenet_wait2 = 250;
uzenet_substate++;
}else{//make sure the send worked
if(UartUnreadCount() > 1 && UartReadChar() == 'O' && UartReadChar() == 'K'){//it worked
uzenet_step++;
uzenet_wait2 = 0;
uzenet_iteration = 0;
eeprom_data[uzenet_iteration*10] |= 128;//make sure we don't try to send this again
}else if(--uzenet_wait2 == 0){//timed out start from the top
}
}
}else if(uzenet_step == 13){//REQUEST UPDATED LIST
if(uzenet_substate == 1){
Uzenet_SendStringP(PSTR("AT+CIPSEND=7\r\n"));//EMULATOR HACK////////////////////////////////////
uzenet_wait_ticks = 2;//wait for '>'
uzenet_substate++;
}else{
Uzenet_SendByte(0xC7);//magic number
Uzenet_SendByte(0x03);//read command
Uzenet_SendByte(0);//start at top of list
Uzenet_SendByte(10);//number of bytes we will be reading 2 score bytes + 8 name bytes
Uzenet_SendByte(5);//5 entries total = 50 bytes
Uzenet_SendStringP(PSTR("\r\n"));
uzenet_wait2 = 255;
uzenet_substate = 1;
uzenet_step++;
}
}else if(uzenet_step == 14){//GET UPDATED LIST
if(uzenet_iteration == 1){//eat "+IPD,X:"
if(UartUnreadCount() && UartReadChar() == ':')
uzenet_iteration++;
else if(--uzenet_wait2 == 0)//timed out, try it all again from the top
uzenet_step = 0;
}else if(uzenet_iteration == 2){
if(UartUnreadCount() >= 25){
uzenet_wait2 = 255;
for(uint8_t i=(3*10);i<(3*10)+25;i++){
eeprom_data[i] = UartReadChar();
}
uzenet_iteration++;
}else if(--uzenet_wait2 == 0)//timed out
uzenet_step = 0;
}else if(uzenet_iteration == 3){
if(UartUnreadCount() >= 25){
uzenet_wait2 = 255;
for(uint8_t i=(3*10)+25;i<(3*10)+25+25;i++)
eeprom_data[i] = UartReadChar();
uzenet_iteration++;
}else if(--uzenet_wait2 == 0)//timed out
uzenet_step = 0;
}else{//got the full update, repeatedly attempt to send our scores and get updated list
uzenet_got_records = 1;//make sure high screen shows this now
uzenet_step = 12;//go back to send any updated data
uzenet_iteration = 0;
uzenet_wait_ticks = 1;
uzenet_wait2 = 0;
}
}
}
This is the current code I am testing, really not too many cycles per frame, and flash usage isn't that bad either at < 1.5k. There are some weird mixtures of single and multimode connection strings you might notice, I am using the old emulator version I put up a while ago since the current multithreaded version is broken still. I am procrastinating finally fixing that once and for all, it has really turned out to be difficult to get UART to work realistically with any sort of speed but doable.
I also plan on releasing the very low performance "script method" which takes less code space and can work with extremely meager ram costs for the buffers. It literally picks a baud rate, then runs all commands with max wait periods, and after the wait, assumes each step works out. It then picks another baud rate and runs the script again, indefintely. This way, if you have the module on there and it was setup correctly and your wifi is working...eventually it would accomplish the task. Should be possible to make it work with baud rates as low as 4800, but this will require "flow control" on the server to be implemented so it doesn't choke out a small receive buffer. Depending on how many iterations it takes to guess the 8266's baud rate, I would estimate it would take approximately 60 seconds to successfully upload a high score in the background and get a new list back(due to using long waits instead of checking responses). Could probably get away with 8 byte Tx buffer and 8 byte Rx buffer once the server supports flow control. That's just theory right now, but I do believe it is at least *possible* for essentially any game no matter what. I don't think implementing pad states networking system is any more complex than the code I posted.